HTTP Caching Headers Guide
Cache-Control Directives
public
Response can be cached by any cache (browsers, CDNs, proxies). Use for static assets shared across users.
Cache-Control: public, max-age=31536000
private
Response is for a single user only. CDNs and shared caches must not store it. Browser cache is OK.
Cache-Control: private, max-age=600
no-cache
Cache may store the response but must revalidate with the origin server before each use (via ETag or Last-Modified).
Cache-Control: no-cache
no-store
Cache must not store any part of the request or response. Used for sensitive data (banking, PII).
Cache-Control: no-store
max-age=<seconds>
Maximum time (in seconds) the response is considered fresh. After this, the cache must revalidate.
Cache-Control: max-age=3600 (1 hour)
s-maxage=<seconds>
Like max-age but only for shared caches (CDNs, proxies). Overrides max-age for shared caches.
Cache-Control: public, max-age=60, s-maxage=3600
immutable
Response body will never change. Browser should never revalidate, even on reload. Use with hashed filenames.
Cache-Control: public, max-age=31536000, immutable
must-revalidate
Once stale, cache must not use the response without revalidation. Prevents serving stale content during outages.
Cache-Control: max-age=600, must-revalidate
stale-while-revalidate=<seconds>
Serve stale content while asynchronously revalidating in the background. Improves perceived performance.
Cache-Control: max-age=60, stale-while-revalidate=300
Vary Header
The Vary header tells caches that the response may differ based on certain request headers. Each unique combination of Vary header values creates a separate cache entry.
Common values:
Warning: Too many Vary values fragment the cache and reduce hit rates.
Common values:
Vary: Accept-Encoding — cache gzip and non-gzip separatelyVary: Accept-Language — cache per languageVary: Authorization — effectively disables shared cachingVary: * — never cache (each request is unique)Warning: Too many Vary values fragment the cache and reduce hit rates.