Caching and conditional requests

Cache-Control in plain language, ETags versus Last-Modified, and revalidation with 304 responses.

Controlling freshness

Cache-Control: max-age=3600, public
Cache-Control: no-store          # never keep it (sensitive data)
Cache-Control: no-cache          # store, but revalidate every time
Cache-Control: private, max-age=600
Cache-Control: immutable, max-age=31536000  # fingerprinted assets
DirectiveEffect
max-age=nFresh for n seconds
s-maxage=nSame, for shared caches (CDN) only
public/privateWhether proxies may cache it
no-cacheCache it, but always revalidate first
no-storeDo not store at all
stale-while-revalidate=nServe stale, refresh in the background
πŸ’‘
no-cache does not mean 'do not cache' β€” that is no-store. The naming has confused people for decades.

Conditional requests

Once a response goes stale, the client asks 'has this changed?' rather than downloading it again. Confirmation costs a tiny 304 with no body β€” the single biggest bandwidth saving available.

# first response
HTTP/1.1 200 OK
ETag: "a1b2c3"
Cache-Control: max-age=0, must-revalidate

# later request
GET /api/profile HTTP/1.1
If-None-Match: "a1b2c3"

# unchanged response - no body transmitted
HTTP/1.1 304 Not Modified
PairValidator
ETag / If-None-MatchVersion fingerprint β€” precise
Last-Modified / If-Modified-SinceTimestamp β€” one-second resolution

A pragmatic setup

  • Hashed asset filenames (app.a1b2c3.js) can be cached for a year with immutable.
  • HTML should be no-cache or short-lived so deploys take effect immediately.
  • The CDN pattern: Cache-Control: public, max-age=0, s-maxage=86400, stale-while-revalidate=60.
  • Never cache personalized responses (Cache-Control: private) in a shared CDN.

FAQ

Users see an old version after deploy.
Cache-Control on HTML is too long, or a hard refresh is needed. Use fingerprinted asset URLs plus short-lived HTML β€” that combination invalidation-free.
Does a query string break caching?
It is part of the cache key for most caches. Some CDNs let you ignore selected parameters; do that deliberately, never accidentally.

HTTP headers HTTPS and TLS

Last refreshed 2026-09-17.