Reference guide · performance · Published 2026-08-15 · 3 min read

HTTP caching headers explained

Understand HTTP caching: Cache-Control and max-age, ETag and Last-Modified revalidation, Vary and stale-while-revalidate, per asset type.

How HTTP caching works

An HTTP cache, the browser, a proxy, an origin shield, or a CDN edge, stores a response and reuses it when the same URL is requested again, as long as the response's headers say it is fresh. A well-cached site issues repeat visits almost entirely from the cache; a badly-cached one redispatches every asset to the origin. Two directives, Cache-Control and the revalidation headers (ETag, Last-Modified), control the whole game.

Cache-Control

Cache-Control is the primary header. The values combine with a comma:

DirectiveMeaning
no-cacheCache the response, but always revalidate with the origin first
no-storeNever store (auth, transactions)
max-age=3600Consider fresh for 3600 seconds
public / privateShared caches can store / only the browser may
must-revalidateOn a stale response, must revalidate before reuse
stale-while-revalidate=60Serve stale while a background revalidate runs

A lifetime larger than a request is the point of caching. For a file that changes every release, use max-age in the future and a content hash in the filename so URL and content change together:

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

Revalidation: ETag and Last-Modified

When a cached response goes stale, the browser sends a conditional request. With ETag, it sends If-None-Match: "<etag-value>"; with Last-Modified, If-Modified-Since: <date>. If the resource is unchanged, the origin answers 304 Not Modified with no body, and the browser keeps the cached copy. 304 responses are cheap; recomposed bodies are not.

HeaderProvided byLifetime
ETagServer generates from the file hash or inodePrecise, byte-for-byte
Last-ModifiedA date the server knowsCoarse (1 second granularity)
304 Not ModifiedOrigin, after validationRemoves the re-download

Prefer ETag when the server can compute it, and otherwise fall back on Last-Modified. If both are present, revalidation uses the stronger.

Vary

Vary tells caches that the response varies with the request headers named. The common cases: Vary: Accept-Encoding keeps a gzip and a brotli version apart, and Vary: Cookie separates a personalized page per user. A Vary header that changes often shuts caching down; the sane default for a shared resource is no Vary, or Vary: Accept-Encoding only.

Pick a policy per asset

AssetCache policy
HTMLno-cache so validation catches changes, or short max-age
Images, CSS, JS with hashed URLspublic, max-age=31536000, immutable
Logged-in / admin responsesno-store
Third-party from a different hostFollow its own headers, or use the CDN config

stale-while-revalidate

stale-while-revalidate serves a stale copy instantly while a background refresh refreshes the cache in the background. It is a big win for data that must stay fast even when the origin is slow, at the price of serving a moment-old version to one user.

Combine these pieces and the happy path: a hashed asset is immutable, a revalidated HTML page costs a 304, and the CDN guide shows where the edge turns that policy into repeat callers never touching the origin.

Need a website built, fixed, optimised, migrated or replaced?

This technical resource is written by CSMBAC, a small design and development studio. If you would rather hand the problem to a professional, the website service page explains how we build enquiry-ready websites.

Explore website services