Reading view

The mental model that finally made WordPress caching layers click for me (OPcache vs object vs page)

I spent a while stacking caching plugins and wondering why things weren't as fast as they should be. The thing that fixed it wasn't a plugin, it was understanding that the three caching layers do completely different jobs and you build them from the bottom up.

OPcache is the foundation. It caches compiled PHP bytecode so the interpreter isn't recompiling your code on every request. It helps every single PHP app, it operates independently of everything else, and you basically never turn it off. In production I set validate_timestamps=0 and just flush it on deploy.

Object caching (Redis or Memcached) sits above that. It stores the results of expensive database queries so WordPress isn't making dozens of DB round trips per page. This is the layer that matters most for logged-in users and anything dynamic, because those requests skip page caching entirely.

Page caching (Nginx FastCGI cache for me) is the big hitter for anonymous traffic. A cached page never touches PHP or MySQL, it just serves HTML. Massive for traffic spikes, but useless for logged-in users, so you set proper bypass rules for wp-admin, carts, checkout and logged-in cookies.

The lightbulb moment was realizing they don't compete, they cover for each other. Page cache handles anonymous hits, object cache carries the cache misses and logged-in users, OPcache speeds up all the PHP underneath both. The mistakes I'd been making were running two page cache solutions at once and expecting object cache to help on pages that were already fully page-cached (it doesn't, page cache bypasses WordPress completely).

The other thing that clicked: the right strategy depends on your site. Marketing sites lean hard on page caching. Membership and ecommerce lean on object caching because everyone's logged in. Frequently updated content sites need shorter TTLs and smart invalidation.

I wrote the full guide up with the actual php.ini, wp-config and Nginx config snippets in this article Understanding WordPress Caching Layers: A Developer's Configuration Guide. Curious how you guys handle cache invalidation on fast-moving sites, that's the part I still tweak most.

submitted by /u/anouarabsslm
[link] [comments]
  •  

Caching didn’t fix our high-volume WordPress sites. Database indexing and cursor pagination did

We manage some content-heavy WordPress sites (news portals, big blogs, 10k+ posts) and hit a wall where no amount of page or object caching helped. Turned out the bottlenecks were baked into how WordPress stores data, not something a cache layer could paper over. Sharing what actually moved the needle in case it saves someone a bad week.

Three things were doing most of the damage:

Taxonomy queries. On a site with 50k posts and ~10 tags each, wp_term_relationships balloons to half a million rows. Filtering by multiple taxonomies means expensive JOINs, and without the right indexes MySQL just falls back to full table scans. A composite index on term_taxonomy_id and object_id took some of these from seconds to milliseconds.

Post meta lookups. wp_postmeta gets brutal at scale since every custom field is its own row. Anything that filters or sorts by meta (featured status, view counts, custom dates) JOINs that table repeatedly. Indexing meta_key with a prefixed meta_value (191 chars for utf8mb4) helped a lot. For the really hot fields we ended up denormalizing into a small custom table kept in sync via save_post.

Deep pagination. WordPress uses OFFSET, so page 500 makes MySQL fetch and throw away 10,000 rows before it returns anything. Crawlers hitting deep archives were quietly hammering the DB. Switching to cursor-based pagination with date_query comparisons kept query time flat no matter how deep the page.

Query Monitor on staging plus EXPLAIN to confirm the indexes were actually being used was the workflow that tied it all together.

Happy to share the SQL and WP_Query snippets if anyone wants them, I wrote the whole thing up with code somewhere. Curious what’s worked for others too, especially anyone who’s gone the custom-table route.

submitted by /u/anouarabsslm
[link] [comments]
  •  
❌