WordPress Speed Optimization

WordPress Caching Explained: Page, Object, Browser and CDN

What each cache layer on a WordPress site stores, which pages must never be cached, where Redis fits, and why visitors sometimes keep seeing old content after you clear everything.

WordPress Caching Explained: Page, Object, Browser and CDN

Every uncached WordPress page view runs the same routine. PHP loads core, each active plugin and the theme, asks the database dozens of questions and assembles the HTML. Caching means keeping the result of some part of that routine so it does not have to be repeated. WordPress caching goes wrong when layers are switched on without knowing which part each one keeps, or when something personal lands in a cache meant for everyone.

The layers at a glance

Four layers are in the title. A fifth, the opcode cache, sits quietly underneath them.

LayerWhat it storesWhat it savesTypical tool
Opcode cacheCompiled PHP scriptsCompiling every PHP file again on each requestOPcache, part of PHP
Object cacheQuery results, options and transientsRepeated database queriesRedis or Memcached
Page cacheThe finished HTML of a pagePHP and the database entirelyA caching plugin or the web server
Browser cacheCSS, JavaScript, fonts and images on the visitor's deviceDownloads on the second page viewCache-Control headers
CDNStatic files, and optionally HTML, on servers near the visitorDistance to your serverA content delivery network

They stack. A request meets the CDN first, then the page cache, and reaches PHP only when the outer layers have nothing to serve.

Page caching does most of the work

A page cache stores the complete HTML of a page the first time it is built and hands the same copy to the next visitor. PHP and the database are skipped. For a site whose visitors are mostly anonymous, this one layer matters more than the others combined.

WordPress has no page cache of its own, only a hook for one. When WP_CACHE is true in wp-config.php, WordPress loads wp-content/advanced-cache.php very early, and caching plugins use that file to answer before the rest of WordPress starts. Many hosts cache at the web server instead (Nginx FastCGI cache, Varnish or their own layer), which is faster still.

Two rules save trouble. First, run one page cache. A plugin cache behind a host cache behind a CDN that also stores HTML gives stale pages three places to hide, so find out what the host already does before installing anything. Second, check that it works. Request a page while logged out and read the response headers:

curl -sI https://example.com/ | grep -iE "cache|age|expires"

A header such as x-cache: HIT, cf-cache-status: HIT or an age value shows that a cache answered. Run it twice, because the first request may be the miss that fills the cache. Tools > Site Health also runs a page cache test and reports the response time it measured.

What a page cache must leave alone

A page cache is safe only for pages that look the same to everyone. The following have to be excluded, and a well-behaved cache excludes most of them without being told:

  • Anything for a logged-in user. Caches skip requests that carry the wordpress_logged_in_ cookie. Without that rule, one member's account page can be shown to another.
  • Cart, checkout and account pages. WooCommerce marks them with the DONOTCACHEPAGE constant, which plugin caches respect. A server-level cache or a CDN never sees a PHP constant, so there the exclusion is written by URL and by cookie. The WooCommerce caching guidelines list both.
  • Pages that vary by visitor. Prices by country, a "recently viewed" block, a greeting by name. Load that fragment with JavaScript after the cached page arrives, or exclude the page.
  • Search results, previews and form submissions. Anything with a search query or a POST body should always reach PHP.

Nonces are the subtle case. A WordPress nonce printed into a form or a script expires within a day by default. If the page cache keeps a page for a week, its forms start failing with an expired-link or 403 error. Keep the cache lifetime under about ten hours on pages with forms, or have the form fetch a fresh nonce.

Object caching with Redis or Memcached

During each request WordPress keeps an object cache in memory, so the same option or post is not fetched twice. By default it is thrown away when the request ends. A persistent object cache keeps it between requests in Redis or Memcached, connected through a drop-in file at wp-content/object-cache.php. Transients move there too and stop filling the options table.

It helps exactly where the page cache cannot: logged-in pages, cart and checkout, the admin, REST and AJAX requests. A store, a membership site or an LMS gains a lot from it. A brochure site that serves nearly everything from the page cache gains little. Things that go wrong:

  • Two sites sharing one Redis database. Staging and live will read each other's data unless each has its own key prefix or database number, which most drop-ins take from a constant in wp-config.php.
  • Too little memory. A full Redis either evicts keys or refuses writes, depending on its maxmemory-policy. Give it headroom and an eviction policy suited to a cache.
  • Code that goes around the cache. Direct SQL updates leave the cached copy stale. After a manual database change or a search-and-replace, run wp cache flush.

Browser caching and the CDN

Static files rarely change, so the server should tell browsers to keep them, with a long Cache-Control: max-age on CSS, JavaScript, fonts and images. WordPress helps by adding a ?ver= value to the address of enqueued files. When the plugin or theme version changes, the address changes and the browser downloads the file again. A theme whose stylesheet version never changes is the usual reason a client cannot see the new design without a hard refresh.

A CDN copies those static files to servers around the world and serves each visitor from the nearest one, which is a safe gain for any site with a spread-out audience. Caching the HTML at the CDN as well removes the trip to your server completely. It is then a page cache like any other, with the same exclusions to configure and the same need to be purged when content changes. The WordPress optimization handbook has more on both.

Why visitors still see old content

Storing things is the easy half of caching. Knowing when to throw them away is where sites break. When a post is updated, the cache has to drop that post's page, the home page, the archives, the feeds and anything else that lists it. Good cache plugins do that on the publish hook. They cannot know about a price printed by a custom widget or a stock level changed by an import script.

  • A menu, widget or footer change needs a full purge, because it appears on every page.
  • Stock and prices changed by an import or an ERP sync need a purge call inside that script.
  • With several layers, purge from the inside out: object cache, page cache, then CDN. Purge the CDN first and it refills from a stale page cache.
  • After a deployment, cached HTML can point at CSS files that no longer exist. Make a page cache purge part of every release.
  • Scheduled posts go live through WP-Cron. If cron is unreliable, the post and its purge are both late.

To find which layer holds the stale copy, bypass them one at a time: request the page with a random query string, then straight from the origin server, then logged in.

How much WordPress caching your site needs

A marketing site or blog needs the host's page cache or one plugin, long browser cache headers and a CDN for static files. That is an afternoon of setup you can do yourself.

A store, a membership site or a course platform should add a persistent object cache and spend the time on exclusions and purge rules, because its most important requests cannot be page-cached. Mistakes show up as a customer seeing someone else's cart, so test logged in, logged out and with items in the cart. Our article on WooCommerce speed optimization goes through the store-specific rules.

When custom code writes to the database outside the usual WordPress functions, the cache rules have to be designed together with that code. That design is part of our performance optimization work, and it is the point where a developer is worth involving.

Frequently asked questions

Do I need a caching plugin if my host already caches pages?

Not for page caching, and running both tends to produce stale pages. Ask the host what their cache covers and how to purge it. A plugin can still be useful for other jobs, such as connecting an object cache, with its page cache feature switched off.

Can pages be cached for logged-in users?

Only with a cache that keeps a separate copy per user or per role, and only for pages that are identical within that group. It uses far more storage and is easy to get wrong. For logged-in traffic, a persistent object cache and faster PHP are the safer route.

Is Redis better than Memcached for WordPress?

Both work well as a WordPress object cache. Redis can write its data to disk and come back from a restart with the cache intact, and Memcached is simpler to run. The practical answer is to use the one your host supports and monitors.

How often should the page cache be cleared?

Rarely by hand. A correctly configured cache purges the affected pages when content changes and expires the rest on a timer. Clear everything after theme or plugin updates, menu changes and deployments.

Add WorldWin Coder to your preferred sources

Add worldwincoder.com as a preferred source on Google, or open this article in your AI assistant to use it as a source.

Swati Priyadrshni
· Founder & CEO at WorldWin Coder

I'm Swati Priyadarshani, Founder & CEO of LMS Advisor and Co-Founder of WorldWin Coder Pvt. Ltd. (est. 2019). Over the last 10+ years, I've helped enterprises build learning platforms that actually get used - not just deployed.

Keep reading

Have something to build, fix or move?

Tell us where you are. A developer reads every inquiry, and you get next steps and a free quote within one business day.