Don't fetch what you already have — freshness lets the browser reuse a copy with no network, and revalidation reuses it with just a 304.
HTTP caching lets the browser skip downloads it doesn't need. A cache sits between the browser and the server and keeps past responses. While a stored copy is still fresh, the browser reuses it with *zero network*; once it goes stale, the browser asks 'has this changed?' instead of blindly re-downloading — and usually hears back 'no'.
When the server sends a file it tags the response with a Cache-Control header. The key directive is max-age, a freshness lifetime in seconds: for that long the browser may reuse its copy without asking the server at all.
max-age=3600 — reuse this copy freely for one hour; it's fresh.no-cache — you may store it, but always revalidate before reuse (it doesn't mean 'don't cache').no-store — never write this to the cache at all (for private or sensitive responses).private — only the browser may cache it, not shared caches (CDNs, proxies).public — any cache along the path may store it.Once a copy is stale, the browser doesn't re-download blindly. On the first response the server attached an ETag — a short fingerprint of the content (it changes whenever the content does) — and often a Last-Modified date. To revalidate, the browser sends a conditional request: If-None-Match: <etag> (or If-Modified-Since: <date>). If nothing changed, the server replies 304 Not Modified with *no body*, so only a few header bytes cross the wire instead of the whole file. The cached copy is reused as-is.
# First request — cache MISS
GET /style.css HTTP/1.1
Host: example.com
HTTP/1.1 200 OK
Cache-Control: max-age=3600, public
ETag: "a1b2c3"
Last-Modified: Wed, 01 Jul 2026 10:00:00 GMT
Content-Length: 18240
... 18 KB of CSS ...
# Later, copy is STALE — browser revalidates
GET /style.css HTTP/1.1
Host: example.com
If-None-Match: "a1b2c3"
If-Modified-Since: Wed, 01 Jul 2026 10:00:00 GMT
HTTP/1.1 304 Not Modified
ETag: "a1b2c3"
# no body — reuse the cached 18 KB