HTTP
HTTP 304Not Modified
HTTP 304 Not Modified is the server's answer to a conditional request: the client sent If-None-Match or If-Modified-Since with its cached copy's validator, and the resource has not changed, so the server sends no body and the client reuses its cache. It is normal, healthy behaviour, not an error.
How a 304 happens
- The client fetches a resource and receives
200 OKwith a validator:ETag: "abc123"and/orLast-Modified: Tue, 15 Sep 2026 10:00:00 GMT. - Later, when its cached copy is stale (per
Cache-Control: max-age) or the user reloads, the client asks again withIf-None-Match: "abc123"orIf-Modified-Since: …. - The server compares. Unchanged →
304 Not Modifiedwith no body, and the client keeps using its copy. Changed →200 OKwith the new content and a new validator.
The cost of a 304 is one small round trip; the saving is the entire body.
What the client can rely on
A 304 may include updated Cache-Control, Expires, ETag and Vary headers, which the client applies to its stored response. It must not include a body.
Configuring validators
Static files: Apache and nginx generate ETag and Last-Modified automatically. Apache's default ETag once included the inode, which broke across servers; FileETag MTime Size avoids that.
Dynamic responses: the application must compute an ETag (a hash of the output or a version number) and compare it to If-None-Match before rendering, otherwise the server does all the work and only saves transfer.
Testing
curl -I https://example.com/style.css
curl -I -H "If-None-Match: \"abc123\"" https://example.com/style.cssThe first shows the ETag; the second, with that ETag supplied, should return 304 Not Modified. Timestamps for If-Modified-Since must be in HTTP date format; the Unix timestamp converter converts epoch values from logs.
Frequently asked questions
Is 304 an error?
No. Browser developer tools show many 304s on a reload; each one is a resource the browser already had and did not need to download again.
Why does my site never return 304?
The server is not sending validators (ETag or Last-Modified), or a proxy strips them, or the client is not sending conditional headers. Check the response headers of a 200 first.
Should I use ETag or Last-Modified?
Both if you can. ETag is precise (content hash or version); Last-Modified has one-second granularity. When both are present, clients send both and the server checks ETag first.