HTTP caching is controlled by a handful of response headers, and small
differences between directives have large effects on performance and
correctness. This reference covers the Cache-Control directives, the
validators (ETag, Last-Modified), the freshness headers (Expires, Age),
and Vary, with notes on how browsers and CDNs interpret each.
How it works
A cache decides whether to reuse a stored response by checking freshness then
validation. Freshness comes from Cache-Control: max-age (or the older
Expires date): while the response age is below the limit, it is served without
contacting the origin. The current age is reported in the Age header.
Once a response goes stale, the cache validates it. If the origin sent an
ETag or Last-Modified, the cache sends a conditional request
(If-None-Match / If-Modified-Since). The origin replies 304 Not Modified
with no body if nothing changed, saving bandwidth, or 200 with fresh content.
Shared caches (CDNs, proxies) honour extra directives — s-maxage, public,
private, proxy-revalidate — that let you cache differently at the edge than
in the browser. Vary tells the cache which request headers produce different
responses so it stores a separate variant per value.
Tips and examples
- Fingerprinted assets:
Cache-Control: public, max-age=31536000, immutable. - HTML you want fresh but cacheable at the edge:
Cache-Control: public, max-age=0, s-maxage=300. - Never cache sensitive responses:
Cache-Control: no-store. - Keep
Varyminimal —Accept-Encodingis fine,User-Agentdestroys hit rates. - Use
stale-while-revalidateandstale-if-errorto hide origin latency and outages from users.