Reference guide · http-status · Published 2026-08-15 · 3 min read
HTTP 304 Not Modified explained
Understand HTTP 304 Not Modified: how conditional requests with ETag and If-None-Match skip re-downloads and speed up repeat visits.
- ·Revalidation step
- ·ETag and If-None-Match
- ·Reading 304 in logs
What 304 Not Modified means
304 Not Modified is the server's answer to a conditional request. When a client already holds a cached copy of a resource and asks "is my copy still current?", a server that still has the same version replies 304 with no response body. The client keeps its stored copy, and the expensive re-transfer of unchanged bytes is skipped.
It is a caching optimization, not an error. Seeing 304s in your logs means repeat visitors are successfully revalidating content from cache rather than re-downloading it. It belongs to the family of successful statuses that a cache-aware setup leans on heavily, and it is the mechanism behind the HTTP caching guide.
How revalidation works
The flow has three steps:
- A resource is stored in the browser or proxy cache with a validator (an
ETagor aLast-Modifieddate). - When the cached copy reaches staleness, the client sends a conditional request carrying the validator in
If-None-MatchorIf-Modified-Since. - The origin compares. If unchanged, it returns
304with empty body and the same validator; if changed, it returns200with the new body.
GET /style.css HTTP/1.1
If-None-Match: "b6a-3f6a"
HTTP/1.1 304 Not Modified
ETag: "b6a-3f6a"
The ETag is the precise validator: the server derives it from the file content, so any change produces a new value and a fresh 200. Last-Modified is the coarse one with one-second granularity. When both are present, the client uses ETag as the stronger signal.
Why browsers see fewer 304s than proxies
A browser with a fresh max-age does not revalidate at all; it serves from cache until the age passes, so it shows zero requests rather than 304s. 304s appear when a resource is stale but unchanged, or when an HTML page uses no-cache (cache, but always revalidate). Proxy caches and the Cloudflare edge also issue 304s to the origin on miss, which is why you may see many 304s in server logs while visitors experience fast, cached page loads.
Reading 304 in logs
- A healthy mix of
200and304on repeat reads means revalidation works; the origin confirms freshness without paying for body transfer. - A sea of
200s with no 304s suggests caching headers are missing orno-storeis set, forcing a full download each visit. - Frequent
304followed by origin load spikes can indicate a too-shortmax-agethat forces revalidation on every request (worth raising for stable assets).
Prefer explicit ETag generation at the origin and a long max-age plus immutable for hash-named assets, reserving conditional revalidation for HTML pages that must pick up changes promptly. Get the headers right and 304 does the cheap revalidation work so the origin only pays bytes when content actually changes.