Cache correctness across browser, server and CDN
Why the cache key is a data boundary
A cache hit is a decision to reuse someone else's computation. Prove equivalence first: which identities, permissions, locales, query parameters, deployment versions and freshness windows can share this value? Optimizing the hit ratio before defining equivalence can optimize a data leak.
Layer inventory and ownership
| Layer | Typical lifetime | Invalidating event |
|---|---|---|
| Request memoization | One request/render | End of request |
| Server data cache | Many requests | Mutation, permission revision, TTL, deployment policy |
| CDN | Shared across viewers | Purge/version change/expiry under provider rules |
| Browser HTTP cache | Browser profile | Validator or freshness policy |
| Client query store | App/session | Mutation, logout, account switch, scope change |
| Service worker | Worker-controlled storage | Explicit version and eviction policy |
The fixture's scoped key includes tenant, user, role, permission revision and resource. This sacrifices sharing to make the security model obvious. A production application may safely share tenant-public data, but must prove that all users sharing the key can see identical fields. A tenant ID alone is insufficient for role-filtered views.
Implementation and race repair
The bounded cache combines TTL, capacity and single-flight requests. Invalidation removes the cached entry and detaches any current in-flight promise. A late response publishes only if it is still the registered promise for that key. Otherwise invalidation can be undone by a request that started before the mutation.
A promise shared across callers also shares failure and underlying work. Do not let one caller abort a request that other callers still need unless you implement reference-counted ownership. The demonstration deduplicates requests; it is not a full client query library.
Use no-store for the personalized HTTP response even when the server internally caches an authorized projection. HTTP headers do not describe or invalidate every application cache. Conversely, a client cache library's invalidation cannot purge the CDN.
HTTP decisions and failure modes
no-cache permits storage but requires validation before reuse; no-store forbids storing the response. max-age describes freshness and s-maxage controls shared-cache freshness. Vary extends response selection using request headers under HTTP semantics; confirm the CDN's actual cache-key policy rather than assuming every provider honors every variation. These semantics come from RFC 9111.
Use content-addressed asset names with long immutable caching and retain previous assets during rollout. A stale HTML shell can reference an old chunk after deployment. Purging HTML does not repair already-open tabs. Offer a safe reload path that preserves drafts when lazy chunk loading fails.
Exercise, answer and rollback
Temporarily replace the key with resource alone and run the tenant-switch test; it must expose the leak. Restore the scoped key, then invalidate while a slow read is pending and prove the old result is not republished. For a CDN experiment, record request headers, response Age, cache status, region and body hash. Local Node tests cannot establish global purge latency. Roll back a broken caching rule by bypassing shared storage first; preserve logs needed to determine exposure scope.
Executable reference and sources
Implementation · Behavioral evidence · Primary source.
Run the projects and assessment. Human review is pending.