HTTP

Comparison

304 Not Modified vs 200 OK

Understand when servers return 304 Not Modified instead of 200 OK. Learn how conditional requests, ETags, and Last-Modified headers enable efficient HTTP caching.

Bottom line: A 304 means the resource has not changed since the client last fetched it — the client uses its cached copy. A 200 means the server is sending the full resource. 304 saves bandwidth; 200 is the baseline response.

304 Not Modified
vs
200 OK

The Core Difference

200 OK is the standard response: the server sends the full resource — headers and body.

304 Not Modified is a cache validation response: the server confirms the resource has not changed since the client last fetched it. No body is sent. The client uses its cached copy.

304 is not an error — it is an optimization. It saves bandwidth and reduces latency by avoiding retransmission of unchanged content.

The Conditional Request Flow

304 only happens when the client sends a conditional request:

# First request — client has nothing cached
GET /styles.css HTTP/1.1

# Server responds with full content + cache validators
HTTP/1.1 200 OK
ETag: "abc123"
Cache-Control: max-age=3600
[full CSS body]

# Later — cache has expired, client revalidates
GET /styles.css HTTP/1.1
If-None-Match: "abc123"

# Resource unchanged — server sends 304, no body
HTTP/1.1 304 Not Modified
ETag: "abc123"
Cache-Control: max-age=3600

Comparison Table

200 OK304 Not Modified
Response bodyYes (full resource)No
Triggered byAny GET/HEADConditional GET (If-None-Match / If-Modified-Since)
Client actionStore in cacheUse existing cached copy
Bandwidth usedFull resource sizeHeaders only (~200–500 bytes)
Requires prior requestNoYes (needs cached ETag or Last-Modified)

ETag vs Last-Modified

Servers can use two mechanisms to enable conditional requests:

ETag — a content fingerprint, usually a hash of the response body:

ETag: "d41d8cd98f00b204e9800998ecf8427e"

Client sends back as If-None-Match: "d41d8cd98f00b204e9800998ecf8427e".

Last-Modified — a timestamp of when the resource last changed:

Last-Modified: Tue, 18 Feb 2026 00:00:00 GMT

Client sends back as If-Modified-Since: Tue, 18 Feb 2026 00:00:00 GMT.

ETags are more reliable. A file can be regenerated with identical content (same ETag, different timestamp) or have its mtime updated without content changes. Use ETags when your server can compute them cheaply.

Cache-Control Interaction

304 only comes into play after a cache entry expires. The flow is:

  1. Fresh cache (max-age not exceeded) — browser uses cached copy directly, no request sent
  2. Stale cache (max-age exceeded) — browser sends conditional request → server returns 304 or 200
  3. No cache validators (no ETag, no Last-Modified) — browser must fetch full 200

Cache-Control: no-cache forces revalidation on every request (step 2 always), but still allows 304 if the server supports it. Cache-Control: no-store disables caching entirely — always 200.

Common Mistakes

Not sending ETag or Last-Modified — without these headers, the browser cannot send a conditional request and will always get a full 200. Add ETags to your static asset responses.

Confusing 304 with 204 — 304 means “not modified, use cache”. 204 No Content means “request succeeded, no body to return” (used for DELETE or empty API responses). They are unrelated.

Expecting 304 for POST — conditional requests only apply to GET and HEAD. API endpoints that use POST will never return 304.