Conditional requests (ETag, Last-Modified, 304)
Send a validator — ETag or Last-Modified — on every cacheable response, and honour If-None-Match / If-Modified-Since so unchanged resources return an empty 304 instead of the full body.
What it is
A conditional request asks the server for a resource only if it has changed. The server attaches a validator to each response — an ETag (an opaque token identifying the body) or a Last-Modified date:
ETag: "87d3702ef9500aacbb5164d5fb9d82a4"
Last-Modified: Mon, 09 Jun 2026 09:00:00 GMT
On the next fetch the client echoes that validator back:
If-None-Match: "87d3702ef9500aacbb5164d5fb9d82a4"
If-Modified-Since: Mon, 09 Jun 2026 09:00:00 GMT
If the resource is unchanged, the server answers 304 Not Modified with empty body and the client reuses its cached copy. If it changed, the server returns the full 200. This is the revalidation step that Cache-Control defers to — see Cache-Control headers.
Why it matters
Cache-Control decides how long a cached copy stays fresh; validators decide what revalidation costs once it goes stale. Without a validator, every revalidation re-downloads the whole body even when nothing changed. With one, an unchanged 2 MB asset becomes a sub-200-byte 304. It is the difference between “is this still current?” costing a header exchange versus a full transfer.
It also matters for agents. A client polling this site’s Markdown mirrors can send If-None-Match and receive a 304 for content that has not changed, instead of re-parsing an identical page every cycle — cheap, polite re-fetching that pairs naturally with stable URLs. This site emits validators on every response; a conditional GET of any unchanged asset returns 304.
How to implement
- Emit a validator on cacheable responses. Most servers and CDNs add
ETagto static files automatically. Confirm it reaches the client and is not stripped by a proxy. - Prefer
ETagoverLast-Modified.Last-Modifiedhas one-second granularity and breaks for content that changes sub-second or has no meaningful file date.ETagis exact. When both are present,If-None-Matchtakes precedence. - Use weak ETags (
W/"…") for negotiated content. A weak validator means “semantically equivalent”, which is correct when the same URL is served gzip to one client and brotli to another. Pair with a correctVaryso caches do not cross the wires — see compression. - Keep the validator stable across identical bytes. It must not change on every request (e.g. derived from a timestamp), or revalidation never short-circuits.
Common mistakes
- No validator at all — revalidation re-downloads the full body every time.
- A strong ETag on content-negotiated responses, so a brotli client and a gzip client fight over one cache entry.
- ETags that vary per request (random or time-based), defeating every conditional request.
- Returning
200with a body when the client’sIf-None-Matchalready matches — wasted bandwidth.
Verification
curl -sI https://example.com/asset.js | grep -i etag— confirm a validator is present.- Re-request with it:
curl -s -o /dev/null -w "%{http_code}" -H 'If-None-Match: "…"' https://example.com/asset.js— expect304. - DevTools → Network → a revalidated resource shows status
304and a tiny transfer size.