Field guide · updated 28 Oct 2026
HTTP Cache Field Guide
Choose cache headers that fit the response—and diagnose “old content” without accidentally sharing private data.
The short answer
Separate storage audience, freshness, stale permission, and matching. Use private, no-cache when a personalized response may stay in a browser but must not enter a shared cache; use no-store when compliant HTTP caches should not intentionally retain it. Add validators for cheap revalidation. When both conditional validators are sent, If-None-Match wins. A stale extension never grants storage or repairs a bad key. Request Cache-Control fields are different: they express advisory client preferences, not response policy or universal origin/purge commands.
What does immutable change for a versioned asset?
It says the representation will not change during its freshness lifetime. A fresh stored response should not be conditionally revalidated by default, including on reload; stale responses return to ordinary revalidation rules. max-age supplies the example lifetime, not immutable. A force reload can be an explicit override.
Standards and documentation review only: no browser, cache, endpoint, or provider was tested. MDN’s Chrome reload caveat is specific to subresources on reload and is not a universal browser claim. Open the concise guide · Read the dated report →
Which stored response does a 304 update?
First build the request-compatible candidate set; stale entries are not excluded just for being stale. A strong validator selects all matching candidates. Without one, a matching weak validator selects only the most recent response; without any validator, only one validator-less candidate can qualify.
Standards-only, synthetic example: a 304 updates selected metadata, not replacement bytes. No cache implementation was tested. Open the practical guide · Read the dated report →
Does proxy-revalidate permit shared reuse of an Authorization response?
RFC 9111 §3.5 lists must-revalidate, public, and s-maxage as shared-cache permissions for responses to requests carrying Authorization. proxy-revalidate is not on that list; it governs validation before stale reuse. Erratum 9004 proposed a no-behavior-change clarification, but its final disposition was not confirmed from the accessible archive record.
Standards and archive review: no cache implementation behavior was tested. Read the dated report →
A missing asset response can be reused—but is not necessarily cached
RFC 9110 makes 404 and 410 heuristically cacheable; HTTP does not require a cache to store them or specify a universal heuristic lifetime. If a cache stores a 404 for a versioned asset, restoring bytes at that same URI may remain hidden while the stored response is fresh. A new no-store response does not purge an older 404, and Age alone does not identify the responding hop.
Documentation-only: no endpoint, browser, provider, cache, or deployment was tested. Open the missing-asset guide · Read the dated report →
A request can express a cache preference; it cannot command every cache
RFC 9111 calls request directives advisory: caches may honor them, but need not. Response directives have a different, mandatory role. Request no-cache is not guaranteed origin revalidation, max-age=0 is not a purge, and no-store does not delete an older entry. only-if-cached is a stored-response preference—not a universal no-forwarding guarantee.
Documentation-only: no authorized runtime or browser/provider experiment was available, so the examples are expected semantics, not live observations. Open the decision-first summary · Read the dated report →
A POST response may serve GET or HEAD—but only under narrow conditions
RFC 9110 requires explicit response freshness and a Content-Location equal to the POST target URI for POST-specific cache eligibility. That permission is for a later GET or HEAD, not another POST; it does not require a cache to store or reuse the response. Separately, an unsafe POST must be forwarded first; a 2xx/3xx response triggers target-URI invalidation only at caches the request traverses.
Expected-only: this is a standards review, not a live cache test. Open the POST response guide · Read the dated report →
Same resource URL, separate browser contexts
Browser HTTP-cache partitioning can make the same URL a separate local candidate under a different top-level site. It does not replace HTTP matching, Vary, freshness, or validation, and it does not configure a CDN or proxy. Use response policy—not partitioning—to control private or sensitive storage.
Documentation-only: examples are expected from standards and engine documentation, not tested. Open the reader summary · Read the dated report →
A moved URL can keep an old redirect target
Each redirect response belongs to the URI requested at that hop. A cached response can retain an older Location; a later redirect in the chain is a separate cache candidate. A new policy does not erase older copies.
Documentation-only: no route, browser, provider, or cache was tested. Open the reader summary · Read the dated report →
When stale HTTP content is allowed
Ask four questions in order: was the response stored, which max-age or shared s-maxage applies, does a bounded stale permission apply, and can the entry validate? stale-while-revalidate and stale-if-error are support-dependent permissions. Under the portable RFC 9111 reading, do not treat them as overrides for no-cache, must-revalidate, proxy-revalidate, or shared s-maxage revalidation.
Documentation-only: all timing rows and header examples are expected, not live measurements. Open the reader summary · Read the dated report →
A worker update does not replace an open page
A successful worker candidate can wait while old clients use the registration. Forced activation can change their controller without replacing their in-memory code. Cache Storage is script-managed and is not cleared by activation; retain old assets until the application compatibility rule allows cleanup.
Documentation-only: no app, browser, origin, or lifecycle test was supplied. Open the reader guide · Read the dated report →
Deploy safely and diagnose old content
Use the new provider-neutral runbook for two paths: keep personalized or sensitive responses out of shared storage, or ship public and versioned content with explicit browser/shared freshness, validators, and named-hop evidence.
Documentation-only: no live provider capture was available. Open the runbook guide · Read the dated report →
Four decisions before shipping a cacheable response
| Decision | Question | Useful starting point |
|---|---|---|
| 1 · Audience | Private or safely shared? Cookies and authentication can make the body user-specific. | Decide the audience before adding public or s-maxage. |
| 2 · Lifetime | How long is fresh? | Set explicit max-age; shared caches may use a separate s-maxage. |
| 3 · Validation | How does stale content recover? | With both validators, If-None-Match is evaluated before If-Modified-Since. |
| 4 · Variants | What changes the body? | Keep target URI, Vary-nominated fields, cookies, authorization, and unlisted application inputs from creating a privacy mismatch. |
Pick a response class
| Response class | Starting policy | Guardrail |
|---|---|---|
| Personalized | private, no-cache | A browser may retain a copy; a shared cache must not. Use no-store when intentional HTTP-cache retention is unacceptable. |
| Public dynamic | public, max-age=60, s-maxage=10 | Only when the representation is safe to share and request variants are accounted for. |
| Versioned asset | public, max-age=31536000, immutable | Fingerprint the URL and deploy a new URL for changed bytes. |
| Must not store | no-store | Compliant caches should not intentionally store or use it; it does not erase an older entry or encrypt the network. |
Locate the serving layer
An old response may come from history/bfcache, a service worker/Cache API, the browser HTTP cache, an intermediary, or the origin. Start by asking whether a new fetch happened; then seek positive, named evidence rather than inferring from 200, a DevTools label, or a missing header.
Open the serving-layer diagnosis guide → · Read the dated report
Cloudflare Cache Rules and Origin Cache Control
Cloudflare adds product-specific controls around ordinary HTTP semantics: Origin Cache Control, the matching Cache Rule's Edge Cache TTL, Browser TTL, cache-key dimensions, and purge scope must be read separately.
Documentation-only: no zone, authorized response, or purge was available. Open the Cloudflare guide page · Read the dated report →
Can a CDN use a different cache lifetime?
RFC 9213 defines CDN-Cache-Control as a targeted policy, distinct from ordinary shared-cache s-maxage. A targeted cache selects the first valid non-empty field on its ordered list; its policy replaces ordinary Cache-Control/Expires for that cache. Cloudflare documents a proprietary field ahead of the standard CDN field, with product-specific forwarding and override rules.
Documentation-only: synthetic freshness example; no cache, zone, or endpoint was tested. Open the targeted cache-control guide · Read the dated report →
CloudFront TTL policy and invalidation
For a named CloudFront behavior, inspect the effective minimum/default/maximum TTLs and cache key. A positive minimum can override origin no-store, private, or no-cache; the audit also narrows the standalone s-maxage documentation gap and keeps viewer headers separate from edge evidence.
Documentation-only: no distribution, cache capture, or invalidation was run. Open the CloudFront guide page · Read the audit refresh →
Use the cache freshness calculator
Need to compare a browser max-age with a shared-cache s-maxage? Open the Cache-Control freshness calculator → for the interactive inputs, formula, browser/CDN explanation, worked examples, FAQ, and limits.
It is a teaching calculation for supplied lifetime and age values—not a complete RFC 9111 current-age calculation, a security boundary, or a vendor configuration test. The original sandboxed tool remains at the existing tool URL.
To calculate current age from Date, Age, and timing instead of supplying an age, see the RFC 9111 Age/current_age guide and its synthetic worked example.