Server Cache (Page Cache)
Server cache (page cache) stores the final HTML output for a URL so repeat requests can be served without running PHP or hitting the database. For most WordPress sites, this is the biggest TTFB win because it removes the entire WordPress bootstrap for cache hits.
What a Cache Hit Looks Like
Request -> Web server
-> Cache lookup
HIT: return cached HTML (no PHP/DB)
MISS: run PHP -> WordPress -> DB -> build HTML -> store -> return
What Should (and Shouldn't) Be Cached
Cache is safest and most effective for:
- Public pages for anonymous visitors (home, posts, categories, product pages)
Cache should be bypassed for:
POSTrequestswp-admin,wp-login.php- Logged-in sessions
- WooCommerce cart/checkout/account
- Requests that change state (example:
?add-to-cart=)
If you cache personalized pages publicly, you can serve the wrong content to the wrong user. Always treat authentication cookies and cart/account routes as cache-bypass until proven safe.
Implementation Patterns
- LiteSpeed (LSCache)
- Nginx FastCGI Cache
- Varnish
LiteSpeed has first-class WordPress support through the LiteSpeed Cache (LSCache) plugin.
Minimum baseline:
- Enable page cache for guests
- Exclude cart/checkout/account
- Keep cache purge predictable
Verify with headers:
curl -I https://example.com/ | grep -i x-litespeed
Typical signals:
x-litespeed-cache: hit
FastCGI cache stores the HTML output produced by PHP-FPM and serves it on subsequent GET/HEAD requests.
Example (simplified) configuration:
fastcgi_cache_path /var/run/nginx-cache levels=1:2
keys_zone=WORDPRESS:100m
max_size=1g
inactive=7d
use_temp_path=off;
fastcgi_cache_key "$scheme$request_method$host$request_uri";
server {
set $skip_cache 0;
if ($request_method = POST) { set $skip_cache 1; }
if ($query_string != "") { set $skip_cache 1; }
if ($http_cookie ~* "wordpress_logged_in|comment_author|wp_woocommerce_session") { set $skip_cache 1; }
if ($request_uri ~* "/cart|/checkout|/my-account") { set $skip_cache 1; }
if ($request_uri ~* "add-to-cart=") { set $skip_cache 1; }
location ~ \.php$ {
fastcgi_cache WORDPRESS;
fastcgi_cache_valid 200 7d;
fastcgi_cache_bypass $skip_cache;
fastcgi_no_cache $skip_cache;
add_header X-FastCGI-Cache $upstream_cache_status;
fastcgi_pass unix:/var/run/php/php8.2-fpm.sock;
include fastcgi_params;
}
}
Verify:
curl -I https://example.com/ | grep -i x-fastcgi-cache
Varnish is an HTTP reverse proxy cache. It is extremely fast, but it adds an extra operational layer (VCL rules, purge workflows, health checks).
Use Varnish when:
- You already operate reverse proxies and want an explicit caching tier
- You need advanced caching logic beyond what your origin server provides
Verify (example):
curl -I https://example.com/ | grep -iE 'x-varnish|age|via|cache'
Purge Strategy
Cache only works when you purge predictably.
- Prefer selective purges (page + related archives) instead of purging everything.
- Purge after theme/plugin updates.
- If you publish frequently, consider a warmup/crawler to avoid cold-cache spikes.
Example (LSCache via WP-CLI):
wp litespeed-purge all
WP-CLI purge commands depend on your cache plugin exposing a WP-CLI command.
Common Problems
| Symptom | Likely Cause | Fix |
|---|---|---|
| Cached pages still slow | Cache misses | Check bypass rules, query strings, and cookies; verify headers |
| Logged-in users see cached HTML | Missing cookie bypass | Bypass when wordpress_logged_in_ is present |
| Cart/checkout issues | Transactional routes cached | Exclude /cart, /checkout, /my-account and test flows |
| Low hit rate | Too many variants | Reduce unnecessary varies (device, query strings, language) |