Every cache so far worked inside PHP. A page cache is different: it stores the finished HTML response whole and hands it to the next visitor as-is, without booting WordPress or opening a database connection.
That is why this layer only pays off in front of PHP. nginx’s FastCGI cache, or a CDN ahead of it, is the right place. A page cache implemented as a plugin answers after PHP has booted and plugins have loaded, so much of what there was to save has already been spent.
What stacking two of them does
Add a plugin cache where the server already caches pages and the same response now has two copies with different lifetimes. Publish an edit and the plugin clears its copy, while the server keeps serving the old HTML until its own expiry.
The symptom always looks the same: the admin sees the new page, visitors see the old one. Because the administrator is logged in and therefore bypassing the cache, it reads as “it looks fine on my screen”, and hunting for the cause in application code can burn days.
Which layer holds the stale copy is a header question. Have the server cache report its own state and the check takes a second.
curl -sI https://example.com/ | grep -i '^x-cache'
# HIT -> served by the server cache
# MISS -> the request reached PHP
Spell out the bypass conditions
Most page-cache incidents come from caching something that should never have been cached. Four conditions are non-negotiable.
set $skip 0;
if ( $request_method = POST ) { set $skip 1; }
if ( $http_cookie ~* "wordpress_logged_in" ) { set $skip 1; }
if ( $request_uri ~* "^/wp-admin/|^/wp-json/acme-payments/" ) { set $skip 1; }
fastcgi_cache_bypass $skip;
fastcgi_no_cache $skip;
add_header X-Cache $upstream_cache_status always;
The third line — payment and webhook routes — matters most. If a webhook response called by an external server gets cached, the payment state never updates, and you find out only once orders start piling up. The logged-in cookie condition is equally mandatory: omit it and one user’s personalised screen can be served to somebody else.
Start invalidation narrow
Purging everything on every edit erodes the point of having a cache; purging too narrowly leaves stale cards on listing pages. A practical starting point is the edited post, the archives it belongs to, and the home page, widened when you find a case it misses.
Decide the verification too. Right after publishing, request the same URL twice while logged out: the first should carry the new content, the second should be a cache hit. Skip that and you are running invalidation rules nobody has ever seen work.
Server cache configuration and hardening are covered further in the servers and infrastructure archive, and nginx and Redis configuration is within the scope of our optimization program.
Next part
Sometimes every request stays slightly heavy no matter how many caches you stack. Next: autoload options, and how to find the data that rides along on every single request.