304 Not Modified

The cached copy is still current, so the server sent no body.

What 304 means

304 is the successful outcome of a conditional request. The client asked 'send this only if it has changed' using If-None-Match with an ETag or If-Modified-Since with a date, and the server confirmed nothing has changed. The client reuses what it already has.

It is a bandwidth optimisation rather than an error, and it is why ETags are worth setting: a 304 is a few hundred bytes of headers instead of a full response body. On a resource requested frequently and changed rarely, that is a large saving.

Like 204, a 304 must not carry a body. It should also echo the headers that would affect caching - ETag, Cache-Control, Vary - so the client can update its stored metadata.

In client code, note that a 304 is not an error even though it is not a 2xx. Libraries differ in whether they surface it or transparently return the cached response, and fetch treats it as a normal response.

Headers this status expects

  • ETag - so the client can update its validator.
  • Cache-Control - to refresh the freshness lifetime.
  • Vary - unchanged from the original response.

Should a client retry?

This is a success. Use the cached representation.

FAQ

Is a 304 an error?
No - it means the cached copy is still valid and the server saved you a download. It is the intended successful outcome of a conditional request.
How do I trigger a 304?
Send If-None-Match with the ETag you were given, or If-Modified-Since with the Last-Modified date. If the resource is unchanged, the server responds 304 with no body.
Can a 304 have a body?
No. Like 204, it must not, and sending one can cause clients and proxies to mis-frame the response.

Often confused with

  • 200 OK The request succeeded and the response carries the result.
  • 412 Precondition Failed A conditional request's precondition was not met, so the request was not applied.