304 Not Modified

When a conditional request confirms the resource has not changed since the client's last cached copy, the server responds with 304 Not Modified. The response carries no message body and instructs the client to reuse its stored representation.

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.

Note

304 Not Modified is not a redirect despite sharing the 3xx status class. Redirects (301, 302, 307, 308) send the client to a different URL via the Location header. 304 tells the client to reuse its cached copy of the same resource, and no Location header is sent. Browser DevTools sometimes group 304 with redirects, which adds to the confusion.

Note

"200 (from disk cache)" in DevTools means the browser served the resource from cache without contacting the server, because the Cache-Control max-age has not expired. 304 means the browser contacted the server to revalidate and the server confirmed the cached copy is current. A cached 200 involves zero network requests, while a 304 involves one round-trip.

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.

Note

Each time the server updates a resource, a new ETag is generated. Alternatively, the server returns a Last-Modified date, and the client uses If-Modified-Since instead, removing the need for the server to maintain an ETag history.

SEO impact

When Googlebot receives a 304, the crawler recognizes the content has not changed since the last crawl. The indexing pipeline recalculates ranking signals for the URL but does not re-fetch or re-index the content.

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: www.example.re

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: www.example.re
If-None-Match: "1234000"

Second response

HTTP/1.1 304 Not Modified
ETag: "1234000"

Third request

GET /news.html HTTP/1.1
Host: www.example.re
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

See also

Last updated: April 4, 2026