Pure-reference catalog of cache-coherence patterns across the request path. Defines the five-tier cache stack (browser → CDN → reverse-proxy → application → data store), the per-tier cache-writing patterns (cache-aside, write-through, write-back, write-around, refresh-ahead), and the canonical invalidation strategies (TTL-only, event-driven purge, surrogate keys, version-tagged URLs, soft purge), plus an anti-pattern table and a worked multi-tenant coherence-test example. Deep detail lives in references/: RFC 9111 Cache-Control / Vary / ETag directive tables, the cross-tier test surface, cache-stampede (thundering-herd) mitigations incl. the XFetch formula, and RFC 5861 stale-while-revalidate / stale-if-error semantics. Use for pattern selection, Cache-Control header design, coherence audits, stampede-refresh strategy, and SWR/SIE window design; use a cache-key-collision check when the question is whether two concrete requests collide on a key scheme.
80
100%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
View guide
Passed
No findings from the security scan
stale-while-revalidate (SWR) and stale-if-error (SIE) are
Cache-Control extensions defined in
RFC 5861, widely
implemented by browsers, CDNs (Cloudflare, Fastly, CloudFront), and
reverse proxies (Varnish via grace).
| Directive | When stale-serve happens | Revalidation |
|---|---|---|
stale-while-revalidate=N | Up to N seconds after max-age expires | Background async; client sees stale |
stale-if-error=N | Origin returns 5xx, up to N seconds after max-age | Client sees stale instead of the 5xx |
Per RFC 5861 §3: "caches
MAY serve the response in which it appears after it becomes stale, up to
the indicated number of seconds." Syntax:
Cache-Control: max-age=60, stale-while-revalidate=300.
t < max-age → fresh cache hit.max-age < t < max-age + SWR → stale served and one async
revalidation fires - this is the stampede-mitigation property: only the
first request revalidates, the herd coasts on stale
(stampede.md).t > max-age + SWR → truly stale; next request blocks on origin.Failed-revalidation behaviour differs per vendor (Cloudflare keeps serving stale until the window expires; Fastly surfaces 5xx sooner; Varnish is VCL-configurable) - test the actual vendor.
Per RFC 5861 §4: a stale response "MAY be used to satisfy the request, regardless of other freshness information" on origin 500/502/503/504. Composition:
Cache-Control: max-age=60, stale-while-revalidate=300, stale-if-error=864001-minute freshness, 5-minute background-refresh grace, 1-day serve-stale grace if the origin is down.
Per RFC 9111, must-revalidate forbids serving stale after expiry. It and
SWR are mutually exclusive in spirit; most caches honour the strictest
(must-revalidate wins). For SWR / SIE to work, don't add
must-revalidate.
| Cache | SWR | SIE | Caveat |
|---|---|---|---|
| Cloudflare | Yes | Yes | Honours response + request directives |
| Fastly | Yes (Surrogate-Control or Cache-Control) | Yes | Stale-on-error more aggressive |
| CloudFront | Yes (since 2022) | Yes | SIE needs origin error caching policy |
| Varnish | grace in VCL | stale-if-error | See varnish-test-vtc-syntax |
| nginx | proxy_cache_use_stale updating | ... error timeout | Different keyword |
| Browsers | Yes | Yes | Per-tab behaviour varies; test |
| Service Workers | Manual (Workbox SWR strategy) | n/a | Code-level implementation |
| Behaviour | Test |
|---|---|
| SWR serves stale within window | max-age=1, SWR=300; wait 5s; request → stale + async revalidate |
| SWR triggers exactly one revalidation | Origin sees one revalidate after the stale response returned |
| SWR window enforced | Wait > max-age + SWR; next request blocks on origin |
| SIE serves stale on 5xx | Origin down; request within SIE window → 200 with stale data |
| SIE window enforced | Origin down beyond window → user sees 5xx |
| must-revalidate wins over SWR | Both set → no stale served |
| Stampede mitigation under load | N=1000 concurrent at t=max-age+1s → origin sees 1-2 revalidates |
| Anti-pattern | Why it fails | Fix |
|---|---|---|
must-revalidate, stale-while-revalidate=300 | Contradictory; SWR silently ignored | Drop must-revalidate |
SWR on private user data without private | Stale exposure risks | Pair with private deliberately |
| SWR=0 | No grace; equivalent to omitting | Use ≥30s |
| SWR window >> max-age (×10+) | Stale for most of the lifetime | Keep proportionate |
| SIE without an origin-5xx alarm | "Site looks fine" while origin is down for days | Pair SIE with monitoring |
no-store overrides everything.Warning header survives (many
CDNs strip it).proxy_cache_use_stale:
nginx.org/en/docs/http/ngx_http_proxy_module.html