A practical guide to HTTP caching headers
Caching headers are one of those topics that seem simple until a stale stylesheet survives three deploys. This is the short version I wish I had read earlier.
Two kinds of files
Split your files into those whose names change when their content changes (fingerprinted assets such as app.3f9c1a.css) and those whose names are stable (index.html). They need opposite treatment.
Cache-Control: public, max-age=31536000, immutable # fingerprinted assets
Cache-Control: no-cache # HTML documents
no-cache does not mean “do not store”. It means “store, but revalidate before use”, which is exactly what you want for a page that should update promptly but not be re-downloaded when nothing changed.
Validators
With ETag or Last-Modified the browser can send If-None-Match or If-Modified-Since and receive a tiny 304 Not Modified instead of the whole body. Static file servers set both automatically; just make sure a proxy in front of them does not strip them.
Common mistakes
- Long
max-ageon a file whose name never changes. - Setting
Vary: *, which effectively disables shared caches. - Forgetting that a CDN has its own cache and needs its own purge step.
Checklist
Fingerprint your assets, cache them for a year, revalidate your HTML, and test with a hard reload and a normal one. The two behave differently.