Reliable HTTP caching starts by deciding who may reuse a response, for how long and how freshness will be validated; it does not start with a copied kitchen-sink header.
RFC 9111 defines caching behavior, while browsers and managed caches add operational controls. A response can be stored yet require validation, remain fresh for a period, or be excluded from storage. The correct policy depends on whether the URL is versioned, the body is personalised and the origin can produce validators.
Freshness and validation solve different problems
A fresh response can be reused without contacting the origin. A stale response may be validated with ETag/If-None-Match or Last-Modified/If-Modified-Since, allowing a 304 response when unchanged. Cache-Control: no-cache permits storage but requires validation before reuse in the relevant context; no-store instructs caches not to store the response. Their names are easy to misread.
Version static assets instead of purging by hope
When a file URL changes with its content, such as app.4f3a.js, the response can use a long freshness lifetime and immutable. A deployment references the new URL; old documents can still load the old asset. Do not apply that policy to stable HTML URLs whose content changes without a new URL, because clients would keep stale pages until freshness expires.
Protect personalised responses
Private marks a response for a private cache rather than shared intermediaries; it does not mean encrypted or secret. Authenticated HTML and API responses need explicit analysis of cookies, authorisation and cache keys. Vary tells caches which request headers select a representation, but varying on high-cardinality or uncontrolled headers can fragment the cache and still miss application-level identity.
Verify the entire request sequence
Inspect the first response, then repeat while fresh and after forced or natural revalidation. Record Age, Cache-Control, ETag, Last-Modified, Vary and status. Test anonymous and authenticated cases separately. A CDN purge is a product operation, not a general deletion mechanism defined by HTTP; design URLs and lifetimes so correctness does not depend on emergency purges.
Starting cache-policy matrix
| Response | Typical starting point | Verify |
|---|---|---|
| Hashed JS/CSS/image | public, long max-age, immutable | URL changes when bytes change |
| Public HTML | no-cache plus validators | 304 occurs when representation is unchanged |
| Short-lived public API | explicit short freshness and validator | Shared cache key includes real variants |
| Personalised response | private/no-cache or no-store after analysis | No cross-user shared-cache reuse |
Implementation review
- Classify each response by audience and mutability.
- Give every response an explicit policy.
- Use validators where revalidation is useful.
- Limit Vary to real representation dimensions.
- Test sequences at browser, CDN and origin boundaries.
Examples were reviewed against RFC 9111 and MDN on 4 September 2026. Treat them as policy patterns, then confirm the behavior of the browser, framework and managed cache actually deployed.
