Team enables full-page cache. TTFB drops from 800ms to 40ms — victory. Except "Hello Admin" banner appears to anonymous visitors, cart counter shows 0 for everyone, and A/B test serves variant B to 100% of users for six hours.
Dynamic page cache is not a switch. It is a variability contract: which HTML parts are identical for which user segments, and for how long. Poorly defined, it speeds the site while creating silent bugs — worse than honest slowness.
Matrix: cacheable or not?
| Content | Full-page cache? | Alternative |
|---|---|---|
| Published static article | Yes (TTL + purge) | Long CDN cache |
| Editorial home list | Yes short TTL | Purge on publish |
| Search results | Rarely (query string) | Normalised key cache |
| Account / admin page | No | Cache bypass |
| Cart / checkout | No | no-store |
| Real-time stock price | Not on shell | AJAX fragment |
Golden rule: if Set-Cookie or Vary: Cookie is needed, edge full-page cache is suspect.
Cache keys: what differentiates responses
Naive URL key enough for blog. For dynamic site, combine URL, language, device class, A/B bucket and auth segment — not user ID in full-page key (too many variants). Query param whitelist. Cache bypass if session cookie present.
Strategies by stack
WordPress: object cache plus page cache, exclude /wp-admin, WooCommerce cart cookies.
Symfony / Laravel: HTTP cache kernel, Redis tag invalidation, ESI for user blocks.
Headless: CDN on public JSON; never on authenticated endpoints without key.
nginx fastcgi_cache: fastcgi_cache_bypass and fastcgi_no_cache if session cookie.
Invalidation: avoid panic "flush all"
TTL alone for low-critical content. URL purge on edit. Tags / Surrogate-Key on CMS publish. Ban pattern on theme overhaul — careful on origin spike. On publish, purge home plus taxonomies plus article.
See HTTP cache headers and Varnish: logged-in users.
CDN plus origin: two levels
Edge cache relieves origin. Consistent Cache-Control headers origin → edge. s-maxage for CDN, max-age for browser. Do not cache responses with Set-Cookie unless explicit edge config.
The climax: cache speeds up or lies — rarely both without design
Measure hit ratio and functional error rate post-cache.
Decide and move forward without blind spots
First classify URLs as cacheable, non-cacheable or fragment. Define keys and test with and without session. Tie invalidation to CMS publish. Audit headers (curl -I) origin and CDN. Monitor HIT/MISS plus stale alerts beyond business TTL. Compare Surrogate-Key compatible hosts via directory.
Frequently asked questions
Cache page with logged-in user?
Rarely full page; public shell plus private AJAX/ESI blocks or strict role keys.
TTL for news site?
Home/lists 60–300s plus publish purge; checkout never full page.
How to invalidate properly?
Event-driven by tag or key; avoid global flush except emergency.
Redis or files for page cache?
Files/Varnish for throughput; Redis for tags; often CDN plus origin.
Dynamic cache starts by listing what must stay wrong — not what can go fast.