304 Not Modified
Usage
The 304 Not Modified status code is sent when the request is safe, such as a GET or HEAD request, and the server determines the resource has not changed since the version stored in the client’s cache. This conserves bandwidth because the server does not retransmit data the client already holds.
The server generates one or more of the following headers in response:
Because the goal of the 304 Not Modified response is to minimize bandwidth, the server omits headers not included in the original request unless they aid the cache-update process.
For conditional GET requests, the relevant directives are If-None-Match and If-Modified-Since. These rely on a validator such as ETag to evaluate whether the resource needs retransmission.
Example
The client requests a resource, receives an ETag identifying the version, then later revalidates. The server recognizes the resource is unchanged and returns 304 Not Modified. On the third request, a new version is available and a full 200 response is sent with an updated ETag.
Stale 304 trap
A 304 confirms the cached version is still current. When the server previously served a broken page (empty body, error content) with a 200 and the crawler cached the result, a subsequent 304 tells the crawler the broken version persists. The crawler stops rechecking because the “content” has not changed. Fixing the page alone is not enough. The server must return a fresh 200 with the corrected body (and a new ETag) to replace the cached error. Debugging this scenario is difficult because the root cause (a past transient error) is no longer visible.
Initial request
GET /news.html HTTP/1.1
Host: example.com
Initial response
HTTP/1.1 200 OK
ETag: "1234000"
Content-Type: text/html
Content-Length: 1250
<message body contains requested resource>
Second request
GET /news.html HTTP/1.1
Host: example.com
If-None-Match: "1234000"
Second response
HTTP/1.1 304 Not Modified
ETag: "1234000"
Third request
GET /news.html HTTP/1.1
Host: example.com
If-None-Match: "1234000"
Third response
HTTP/1.1 200 OK
ETag: "1234001"
Content-Type: text/html
Content-Length: 1600
<message body contains requested resource>
Code references
.NET
HttpStatusCode.NotModified
Rust
http::StatusCode::NOT_MODIFIED
Rails
:not_modified
Go
http.StatusNotModified
Symfony
Response::HTTP_NOT_MODIFIED
Python3.5+
http.HTTPStatus.NOT_MODIFIED
Java
java.net.HttpURLConnection.HTTP_NOT_MODIFIED
Apache HttpComponents Core
org.apache.hc.core5.http.HttpStatus.SC_NOT_MODIFIED
Angular
@angular/common/http/HttpStatusCode.NotModified