304 Not Modified
Применение
Код состояния 304 Not Modified отправляется, когда запрос безопасный — например GET или HEAD — и сервер определяет, что ресурс не изменился по сравнению с версией, сохранённой в кэше клиента. Это экономит трафик, поскольку сервер не передаёт повторно данные, которые у клиента уже есть.
Примечание
304 Not Modified не является перенаправлением, несмотря на принадлежность к классу состояний 3xx. Перенаправления (301, 302, 307, 308) направляют клиента на другой URL через заголовок Location. А 304 сообщает клиенту, что нужно повторно использовать закэшированную копию того же ресурса, и заголовок Location не отправляется. Инструменты разработчика в браузере иногда группируют 304 вместе с перенаправлениями, что усиливает путаницу.
Примечание
Запись “200 (from disk cache)” в инструментах разработчика означает, что браузер отдал ресурс из кэша, не обращаясь к серверу, поскольку срок max-age из Cache-Control ещё не истёк. А 304 означает, что браузер обратился к серверу для повторной валидации и сервер подтвердил актуальность закэшированной копии. Закэшированный 200 не требует ни одного сетевого запроса, тогда как 304 требует одного обмена с сервером.
Сервер формирует в ответе один или несколько следующих заголовков:
Поскольку цель ответа 304 Not Modified — минимизировать трафик, сервер опускает заголовки, не входившие в исходный запрос, если только они не помогают процессу обновления кэша.
Для условных запросов GET значимыми директивами являются If-None-Match и If-Modified-Since. Они опираются на валидатор, такой как ETag, чтобы определить, нужна ли повторная передача ресурса.
Пример
Клиент запрашивает ресурс, получает ETag, идентифицирующий версию, а затем выполняет повторную валидацию. Сервер определяет, что ресурс не изменился, и возвращает 304 Not Modified. При третьем запросе доступна новая версия, поэтому отправляется полный ответ 200 с обновлённым ETag.
Примечание
Каждый раз, когда сервер обновляет ресурс, генерируется новый ETag. Как вариант, сервер возвращает дату Last-Modified, а клиент использует вместо этого If-Modified-Since, что избавляет сервер от необходимости хранить историю ETag.
Влияние на SEO
Когда Googlebot получает 304, поисковый робот понимает, что содержимое не изменилось с момента последнего сканирования. Конвейер индексирования пересчитывает ранжирующие сигналы для этого URL, но не загружает и не индексирует содержимое повторно.
Ловушка устаревшего 304
Ответ 304 подтверждает, что закэшированная версия по-прежнему актуальна. Если сервер ранее отдал сломанную страницу (пустое тело, содержимое с ошибкой) с кодом 200 и робот закэшировал этот результат, то последующий 304 сообщит роботу, что сломанная версия сохраняется. Робот перестаёт перепроверять страницу, потому что «содержимое» не изменилось. Одного лишь исправления страницы недостаточно. Сервер должен вернуть свежий ответ 200 с исправленным телом (и новым ETag), чтобы заменить закэшированную ошибку. Отлаживать такой сценарий трудно, потому что первопричина (прошлая кратковременная ошибка) больше не видна.
Первоначальный запрос
GET /news.html HTTP/1.1
Host: example.com
Первоначальный ответ
HTTP/1.1 200 OK
ETag: "1234000"
Content-Type: text/html
Content-Length: 1250
<message body contains requested resource>
Второй запрос
GET /news.html HTTP/1.1
Host: example.com
If-None-Match: "1234000"
Второй ответ
HTTP/1.1 304 Not Modified
ETag: "1234000"
Третий запрос
GET /news.html HTTP/1.1
Host: example.com
If-None-Match: "1234000"
Третий ответ
HTTP/1.1 200 OK
ETag: "1234001"
Content-Type: text/html
Content-Length: 1600
<message body contains requested resource>
Примеры кода
.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