A short field guide to HTTP caching headers
Caching is one of the few areas where a single header can change your infrastructure bill. It is also where I find the most confident wrong answers, usually in a code review comment that says "just add no-cache".
The three directives that matter
max-age is a freshness lifetime in seconds. immutable promises the bytes
will never change for that URL, which lets browsers skip revalidation entirely.
no-cache does not mean "do not cache"; it means "revalidate before use".
no-store is the directive people actually want when they write no-cache.
# content-addressed asset: never revalidate
Cache-Control: public, max-age=31536000, immutable
# HTML: always revalidate, but allow a cheap 304
Cache-Control: no-cache
ETag: "8f2c1a"
Where it goes wrong
- One lifetime for everything. Versioned assets and HTML want opposite policies, and a single default will be wrong for one of them.
- Ignoring Vary. If your response depends on
Accept-Encodingor an auth cookie, say so, or a shared cache will hand one user another's page. - Trusting a CDN to fix it. A CDN honours your headers; it does not invent a sane policy for you.
The debugging trick that has saved me most often: open the network panel, look at the Size column, and ask why it says "disk cache" or why it does not.