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 - the RFC 9111 Cache-Control / Vary / ETag directive tables and the cross-tier coherence + per-tier test surface - lives in references/. Use for pattern selection, Cache-Control header design, and coherence audits; use a cache-key-collision check when the question is whether two requests in an existing system collide on a concrete key scheme. Consumed by redis-cache-tests, cdn-cache-purge-tests, varnish-test-vtc-syntax, browser-cache-control-tests, and the cache-key-collision check.
75
94%
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
Keeping cached values consistent with their source of truth
across tiers (browser, CDN, reverse-proxy, application, data
store). Wrong coherence shows as stale data; wrong invalidation
shows as cache stampedes per cache-stampede-reference. A
pure reference consumed by per-tier test skills.
Cache-Control directives, Vary
key, and ETag validators per
references/rfc-9111-http-caching-directives.md.| Tier | Where | Common TTL | Invalidation |
|---|---|---|---|
| Browser | Cache-Control: private | minutes-hours | TTL only (or Service Worker code) |
| CDN | Cloudflare / Fastly / CloudFront / Akamai | seconds-days | Purge API or surrogate-key tag |
| Reverse proxy | Varnish, nginx | seconds-hours | VCL purge / nginx cache_purge |
| Application | Redis / Memcached / in-process | seconds-minutes | Direct delete / pub-sub broadcast |
| Data store | Postgres query cache, RDS read replicas | seconds | Replication-driven |
A coherence bug at any tier surfaces at the user. The test surface is layered; each tier needs its own coherence tests.
For application-tier caches (Redis):
| Pattern | Flow | When |
|---|---|---|
| Cache-aside (lazy load) | Read miss → read source → populate → return; Write → invalidate cache | Read-heavy, eventual consistency OK |
| Write-through | Write → write source → write cache (synchronous) | Strong consistency, latency tolerable |
| Write-back | Write → write cache → async write to source | Burst writes; data-loss risk on cache crash |
| Write-around | Write → write source (skip cache); reads do cache-aside | Write-heavy with rare re-reads |
| Refresh-ahead | Background refresh before TTL expires | Predictable read patterns; hot keys |
| Strategy | Mechanism | Trade-off |
|---|---|---|
| TTL-only | Just let it expire | Simple; possibly-stale window = TTL |
| Event-driven purge | Source-of-truth update fires a delete | Coupling; firehose at high write rate |
| Surrogate keys (Fastly, Varnish) | Tag responses; purge by tag | Group-invalidation; coordination cost |
| Version-tagged URLs | /api/users?_v=42; new version = new key | Immutable cache; full deploy per change |
| Soft purge | Mark stale, keep serving until refresh | Used by stale-while-revalidate per stale-while-revalidate-reference |
Scenario: /api/users serves per-tenant dashboard data, is read-heavy,
and must never leak one tenant's rows to another. Walk the four decisions
from How to use this reference, then the test.
Cache-Control: private and the
CDN is bypassed for this route (or keyed per tenant), not left on the
shared edge.Vary: Authorization so each tenant gets a separate
cache entry (the fix for the cross-tenant leak in the Anti-patterns
table), and add a content-hash ETag so an unchanged reload returns
304 Not Modified instead of the full body.Coherence test (the browser-tier "write → reload → see old" case):
/api/users; the response is cached.Authorization header never returns tenant A's cached rows, proving the
Vary: Authorization split holds.| Anti-pattern | Why it fails | Fix |
|---|---|---|
Cache-Control: public on per-user data | Shared cache leaks data | Use private for user-specific |
Missing Vary: Authorization | Cross-tenant leak | Add to Vary or set private |
s-maxage longer than session lifetime | Logged-out users see another user's data | Match TTL to security window |
| TTL but no purge | Stale-window = TTL even for urgent updates | Implement purge API + use surrogate keys |
ETag generated per-request from now() | Defeats the validation | Stable ETag from content hash |
no-cache instead of no-store for sensitive data | Browser still stores; just revalidates | no-store, no-cache, must-revalidate, private |
| Browser TTL = CDN TTL = origin TTL | Multi-tier amplifies staleness instead of layering it | Origin lowest, CDN longer, browser shortest |
| Cache-aside without write-then-invalidate | Reads see pre-write state for TTL window | Always invalidate on write |
Vary: * | Disables shared cache entirely | Use specific headers |
| Single Cache-Control for HTML + JSON + assets | One-size doesn't fit; HTML often short, assets long | Per-route directives |
The contract layer and the audit-and-test surface live in two companion references so this file stays a decision surface:
Cache-Control response directives,
Vary key derivation, and ETag / If-None-Match revalidation:
references/rfc-9111-http-caching-directives.md.immutable:
www.rfc-editor.org/rfc/rfc8246.html.stale-while-revalidate-reference.cache-stampede-reference.cross-tenant-data-leak-tests.redis-cache-tests,
cdn-cache-purge-tests,
varnish-test-vtc-syntax,
browser-cache-control-tests.