caching
Cache-Control
Directs caching behavior in both requests and responses per RFC 9111, controlling freshness, revalidation, and where the response may be stored. Directives can be combined and are respected by browsers, CDNs, and intermediate proxies.
Cache-Control: <directive>[, <directive>...] (e.g. max-age=3600, s-maxage=86400, public, no-cache, no-store, private, must-revalidate, immutable, stale-while-revalidate=<seconds>) Common directives / values
| Directive | Purpose |
|---|---|
| max-age=<seconds> | Response is fresh for this many seconds in any cache. |
| s-maxage=<seconds> | Overrides max-age for shared caches (CDN/proxy) only. |
| public | Explicitly allows any cache to store the response, even if normally not cacheable. |
| private | Only the end-user's browser may cache; forbids shared caches like CDNs. |
| no-cache | Cache may store but must revalidate with the origin before reuse. |
| no-store | Do not store the response in any cache anywhere. |
| must-revalidate | Once stale, must revalidate; do not serve stale on errors. |
| immutable | Response body will not change during its freshness lifetime; suppresses reload revalidations. |
Examples
Cache-Control: public, max-age=31536000, immutable Ideal for fingerprinted static assets (e.g. app.abc123.js) served by a CDN.
Cache-Control: private, no-cache Personalized HTML: browser may store but must revalidate with ETag each visit.
Cache-Control: no-store Sensitive banking/auth responses that must never touch disk or intermediary caches.
Cache-Control: public, max-age=60, s-maxage=600, stale-while-revalidate=86400 Browser caches 1min, CDN 10min, may serve stale for 24h while re-fetching in background.
Gotcha
Cache-Control: private prevents CDN caching but still allows the browser to cache. no-cache does NOT mean 'do not cache' — that is no-store; no-cache means 'store but revalidate before use'.
Related headers
ETag
An opaque identifier for a specific version of a resource, used by clients in conditional requests to avoid re-downloading unchanged bodies. The server compares the ETag against If-None-Match (or If-Match) and returns 304 Not Modified when they match.
Vary
Tells caches which request headers were used for content negotiation, so they know to store and serve separate variants keyed by those headers. Missing or wrong Vary causes users to see cached content meant for someone else (wrong language, encoding, or device).
Last-Modified
Timestamp of when the origin server believes the resource was last modified, used as a weak validator for conditional requests. Clients echo it back in If-Modified-Since to receive a 304 when unchanged.
If-None-Match
Request header that makes the request conditional: the server returns 200 with the body only if none of the listed ETags match the current representation, otherwise 304 Not Modified. In write requests it protects against creating a duplicate resource.