HTTP Cache Header Reference

Search Cache-Control, ETag, Expires and Vary directives with CDN behavior notes.

Interactive reference for HTTP caching headers — Cache-Control directives, validators like ETag and Last-Modified, Expires, Age and Vary — with notes on how browsers and CDNs interpret each one. It runs free in your browser on Gera Tools, with nothing uploaded.

Last updated Source: Gera Tools

What is the difference between no-cache and no-store?

no-store forbids any storage of the response — it must be fetched fresh every time. no-cache allows storage but requires the cache to revalidate with the origin (via ETag or Last-Modified) before reuse. no-cache is about validation; no-store is about not keeping the data at all.

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.

Common directive combinations

Fingerprinted static assets (JS, CSS, images with hash in filename)

Cache-Control: public, max-age=31536000, immutable

Cache forever (one year). The filename changes when content changes, so there is no risk of serving stale content. immutable tells the browser to skip revalidation even on a hard reload — it was served fresh and won’t change. CDNs and browsers both benefit.

HTML pages

Cache-Control: no-cache
ETag: "abc123"

no-cache stores the response but requires a revalidation check before serving it. This ensures users always get the newest page without paying the full download cost — if the ETag matches, the server returns 304 Not Modified and the browser reuses its copy.

API data that can be slightly stale

Cache-Control: public, max-age=60, stale-while-revalidate=30

Serve from cache for 60 seconds. For the 30 seconds after that, keep serving the stale response while fetching a fresh copy in the background. Users always get a fast response; the stale window is visible only to very frequent callers.

Sensitive responses (auth, personal data)

Cache-Control: no-store

Never store the response, not even temporarily. No CDN, proxy, or browser cache will keep a copy.

The Vary header and CDN caching

Vary lists request headers that affect the response, so the cache stores a separate entry per unique combination of values. Vary: Accept-Encoding is safe and common — the cache stores a gzipped and an uncompressed version per URL. Vary: User-Agent is destructive: it creates a separate cache entry for every browser and device string, effectively disabling CDN caching entirely.

If you serve different content for mobile vs desktop, use separate URLs or a cookie-based flag that you control rather than Vary: User-Agent.

Tips

  • Keep Vary minimal — Accept-Encoding is fine, User-Agent destroys hit rates.
  • Use stale-while-revalidate and stale-if-error to hide origin latency and outages from users.
  • s-maxage overrides max-age for shared caches only — use it to cache aggressively at the CDN edge while keeping browsers conservative.
  • The Age response header tells you how old the cached copy is (in seconds since the origin generated it); useful for diagnosing stale-cache issues.