Код 304 Not Modified значит, что страница не изменилась с прошлого запроса и браузер может показать копию из своего кэша. Это не ошибка, а экономия: сервер не передаёт страницу заново, ответ состоит из одних заголовков. Ошибкой 304 называют, когда этот механизм работает неправильно — посетитель видит старую версию сайта или сервер вовсе не умеет так отвечать.
Мы проверили на стенде, как 304 отдают nginx 1.22, Apache 2.4.68 и PHP 8.3 и как его обрабатывает Chromium 149, а затем отправили условные запросы на главные страницы 2056 сайтов, чтобы узнать, сколько из них отвечают 304 на самом деле.
Что значит код 304 Not Modified
Стандарт HTTP (RFC 9110, раздел 15.4.5) определяет код так:
The 304 (Not Modified) status code indicates that a conditional GET or HEAD request has been received and would have resulted in a 200 (OK) response if it were not for the fact that the condition evaluated to false.
Иначе говоря, 304 представляет собой статус ответа на условный запрос протокола HTTP. Обычный запрос «дай страницу» всегда получает 200 с содержимым. Условный говорит «дай страницу, если она изменилась», и если не изменилась — сервер отвечает 304. Тела в таком ответе быть не может: «A 304 response is terminated by the end of the header section; it cannot contain content or trailers».
Код входит в группу 3xx — перенаправления: сервер как бы перенаправляет клиента к его же сохранённой копии. От кодов 301 и 302 он отличается тем, что нового адреса не передаёт.
Где код 304 среди кодов состояния HTTP
Код состояния — трёхзначное число в первой строке ответа сервера. Первая цифра указывает класс ответа, и по ней уже понятно, что произошло:
- 1xx — информационные. Сервер получил часть запроса и продолжает обработку. Например, 100 Continue клиент ждёт перед отправкой больших вложений.
- 2xx — успешные. Запрос выполнен: 200 OK — страница получена, 201 — ресурс создан, 204 — ответ без данных.
- 3xx — перенаправление. Клиенту нужно сделать ещё одно действие: 301 — адрес перемещён навсегда, 302 — временно, 304 — ресурс не изменён, используйте сохранённую копию.
- 4xx — ошибки клиента. 400 — неверный синтаксис запроса, 401 — клиент не авторизован и должен указать логин и пароль, 403 — доступ запрещён, 429 — слишком много запросов.
- 5xx — ошибки сервера. 500 — внутренняя ошибка, 502 — прокси получил неверный ответ, 503 — перегрузка или обслуживание, 504 — истекло время ожидания.
Код 304 стоит в классе 3xx, хотя никуда не перенаправляет. Он не сообщает о неполадках и не требует от пользователя никаких действий. Такое состояние означает нормальную работу кэша: запрос завершён успешно, просто ответ без тела.
Как работает механизм кэширования и условные запросы
При первом визите сервер отдаёт страницу с кодом 200 и одним или двумя заголовками-метками:
- ETag — идентификатор версии файла, например
"6ad43807-4e"; - Last-Modified — время последнего изменения.
Браузер сохраняет страницу вместе с метками. При повторном запросе он отправляет их обратно в заголовках If-None-Match (с ETag) и If-Modified-Since (с датой). Сервер сравнивает: метка совпала — ответ 304, не совпала — 200 с новой версией.
По шагам процесс выглядит так:
- При первой загрузке браузер получает полный ответ 200 и сохраняет его в кэше вместе с ETag и Last-Modified.
- При следующих посещениях он отправляет условный запрос с этими значениями.
- Сервер сравнивает их с текущей версией файла на своей стороне.
- Если в запрашиваемом ресурсе ничего не менялось, возвращается 304 без тела, и браузер показывает загруженную ранее копию.
- Если файл изменился, сервер отдаёт 200 с новой версией и новыми метками, и цикл начинается заново.
Так браузер получает обновление, когда оно есть, и не скачивает страницу, когда его нет. Это существенно снижает количество передаваемых данных: на нашем сайте ответ по условному запросу в десятки тысяч раз меньше полного.
Если пришли оба заголовка, главнее ETag. Стандарт (раздел 13.1.3) прямо требует игнорировать If-Modified-Since при наличии If-None-Match. На стенде nginx так и сделал: чужой ETag при верной дате дал 200, а не 304.
Стенд: как 304 отдают nginx, Apache и PHP
| Сервер и случай | Метки в ответе | Условный запрос |
|---|---|---|
| nginx 1.22, статический файл | ETag и Last-Modified | 304, 0 байт тела |
| nginx, сжатие gzip | слабый ETag W/"…" | 304 |
| nginx, дата в If-Modified-Since не совпадает | — | 200 (по умолчанию нужна точная дата) |
| Apache 2.4.68, статический файл | ETag и Last-Modified | 304 |
| Apache, сжатие mod_deflate | ETag с суффиксом -gzip | 200 — 304 не приходит |
| PHP 8.3, обычный скрипт | никаких меток | 304 не бывает |
| PHP с session_start() | Cache-Control: no-store, Expires 1981 года | 304 не бывает |
| PHP со своим ETag | ETag из кода | 304 |
Отсюда три вывода. Статические файлы оба сервера отдают с 304 сами.
Страницы на PHP — а это большинство сайтов на CMS — по умолчанию его не
получают: скрипт не сообщает ни ETag, ни Last-Modified, и сервер не знает,
с чем сравнивать. А у Apache со сжатием есть грабля: ETag в ответе
меняется на "…-gzip", и с таким значением в If-None-Match сервер
отвечает 200, хотя файл не менялся.
Как Chromium обрабатывает 304
Обычная перезагрузка (F5). Chromium отправил If-None-Match, If-Modified-Since и Cache-Control: max-age=0. Сервер ответил 304, браузер показал копию из кэша. Сама страница при этом видит код 200: браузер подставляет сохранённый ответ, и 304 заметен только в журнале сервера.
Жёсткая перезагрузка (Ctrl+Shift+R). Условных заголовков нет, только Cache-Control: no-cache. Сервер всегда отдаёт 200 и свежую версию.
Переход по ссылке. Страница без Cache-Control, но с Last-Modified десятидневной давности: при повторном переходе по ссылке Chromium вообще не обратился к серверу и взял копию с диска. Браузер сам считает её свежей по возрасту файла — это эвристическое кэширование.
304 без кэша. Если сервер ответил 304 на запрос, где условий не было, Chromium прервал переход с ошибкой ERR_ABORTED. Новая вкладка осталась пустой страницей about:blank — посетитель видит белый экран.
Зачем 304 владельцу сайта
Для владельцев сайтов и предпринимателей 304 — бесплатный способ ускорить сайт без переделки. Он позволяет не передавать повторно то, что у посетителя уже есть: HTML-страницы, картинки, стили и скрипты. Чем больше постоянных посетителей и роботов, тем заметнее экономия.
Что даёт правильная настройка:
- производительность — повторная загрузка идёт быстрее, ведь по сети передаются только заголовки;
- нагрузка — сервер не отправляет одни и те же данные, а на тарифах с оплатой трафика это ещё и счёт меньше;
- сканирование — поисковые роботы периодически обходят сайт и с 304 быстрее находят новые и изменённые страницы;
- актуальность — при правильных метках посетитель получает новую версию сразу после правки, а не через неделю.
Понимание этого механизма полезно и для оптимизации: 304 могут получать не только браузеры, но и мобильные приложения, и роботы.
Однако 304 не гарантия скорости. Если первая загрузка медленная, а большинство посетителей приходят впервые, выигрыш будет небольшим. Основная польза — для постоянной аудитории и для поисковых систем, которые возвращаются к сайту часто.
Когда 304 становится проблемой
Посетители видят старую версию
На стенде мы изменили файл, но сохранили его размер и время изменения. nginx строит ETag из этих двух величин, поэтому метка осталась прежней. По F5 сервер ответил 304, и Chromium показал старый текст. Помогла только жёсткая перезагрузка. Так бывает после выкладки, если программа копирования сохраняет даты файлов, а правка не изменила их размер.
Сервер никогда не отвечает 304
Вторая ситуация обратная: сервер не принимает условные запросы.
Тогда браузеры и роботы каждый раз скачивают страницу целиком. Ошибки
посетитель не видит, но сайт тратит больше трафика и времени. Типичные
причины по нашим замерам включают: PHP-страница без меток, ETag, который меняется
при каждом запросе, и суффикс -gzip у Apache.
Ошибка 304 у пользователя
Сообщение «ошибка 304» браузер сам не показывает: код скрыт от пользователя, а страница открывается из кэша. Признаки неполадок другие — устаревший контент после обновления сайта или белый экран. Если вместо страницы белый экран, а в журнале 304, значит, сервер отвечает 304 без повода. Посетителю стоит обновить страницу через Ctrl+Shift+R или очистить кэш браузера для этого сайта — удалять всю историю и ставить дополнительные программы нет необходимости. Помогает и режим инкогнито: в этом режиме браузер начинает с пустым кэшем. Если проблема возникает у всех посетителей, а не у одного, исправлять её нужно на стороне сервера, а не на компьютере. Проверять компьютер на вирусы и переустанавливать драйверы, как советуют некоторые статьи, бесполезно: код отдаёт сервер, а не Windows.
Обход: сколько сайтов отвечают 304
Мы запросили главные страницы 2056 сайтов из Германии, взяли из ответа ETag и Last-Modified и повторили запрос с ними. Кодом 200 ответили 1469 сайтов.
| Что нашли | Сайтов из 1469 |
|---|---|
| Есть ETag | 393 |
| Есть Last-Modified | 585 |
| Нет ни ETag, ни Last-Modified | 861 |
| Ответили 304 хотя бы на один условный запрос | 490 |
Больше половины сайтов не сообщают меток вовсе, и 304 у них не может быть в принципе. Среди тех, кто ETag отдаёт, 304 по нему вернули 194 из 393. У 184 ETag изменился между двумя запросами подряд — 164 из них стоят за DDoS-Guard. Ещё у 14 метка была той же, а ответ всё равно 200; у 11 из них ETag с суффиксом -gzip, как у Apache на нашем стенде.
По Last-Modified 304 вернули 471 из 585. У 62 сайтов дата менялась при каждом запросе: сервер ставит текущее время, и проверка «изменилась ли страница» всегда отвечает «да».
Наш сайт отвечает 304 по обоим заголовкам: страница статьи по условному запросу весит 0 байт тела вместо 57 595.
Влияние кода 304 на SEO
Google в документации о своих краулерах пишет:
Google's crawling infrastructure supports heuristic HTTP caching as defined by the HTTP caching standard, specifically through the ETag response- and If-None-Match request header, and the Last-Modified response- and If-Modified-Since request header.
Если есть оба заголовка, Google берёт ETag и рекомендует именно его, «as ETag doesn't have date formatting issues». Googlebot использует кэш при повторном обходе страниц для поиска.
В справочнике кодов Яндекс Вебмастера про 304 сказано: «Если страница не изменилась с момента последнего обращения робота, рекомендуется выдавать этот код». Яндекс советует его для изображений, CSS и JavaScript и добавляет: «Это ускорит индексирование и уменьшит трафик».
Правильный 304 не поднимет позиции, но робот тратит меньше времени на неизменные страницы и быстрее доходит до новых — это важно для больших сайтов с ограниченным краулинговым бюджетом. Неправильный 304 опаснее: робот сочтёт изменённую страницу прежней. Дата изменения пригодится и в lastmod карты сайта.
Как устранить проблему 304
- Проверьте ответ. Запросите страницу, возьмите ETag и повторите:
curl -sI -H 'If-None-Match: "6ad43807-4e"' https://сайт/. 304 — всё правильно. - Apache со сжатием. Добавьте в конфигурацию
DeflateAlterETag NoChangeилиRemove. На стенде оба варианта вернули 304 со сжатым ответом. - nginx. Директивы
etag onиif_modified_since exactвключены по умолчанию. Значениеbeforeразрешает 304, если дата файла не позже присланной. - PHP и CMS. Скрипт должен сам отдать ETag или Last-Modified и сравнить их с заголовками запроса. На стенде хватило нескольких строк кода; для WordPress и других CMS есть плагины кэширования.
- Старая страница после выкладки. Это решение для ситуаций, когда новая версия находится на сервере, а посетителям отображаются старые страницы. Убедитесь, что выкладка меняет
время файлов, или задайте
Cache-Control: no-cacheдля HTML — тогда браузер будет спрашивать сервер каждый раз. - За защитой и CDN. Если ETag меняется на каждый запрос, проверьте настройки кэша у провайдера защиты. Такие системы часто применяют свои параметры кэширования поверх настроек сервера.
- После исправления повторите проверку из пункта 1 и посмотрите журнал сервера: при повторных посещениях в нём должны появиться ответы 304. Это самый простой инструмент проверить результат.
Источники
- RFC 9110, разделы 15.4.5, 13.1.3, 13.2.2 — rfc-editor.org
- Google: обзор краулеров, раздел HTTP Caching — developers.google.com
- Яндекс Вебмастер: справочник HTTP-кодов — yandex.ru/support/webmaster
- nginx: директивы etag и if_modified_since — nginx.org/ru/docs
Похожие ошибки
- ERR_CACHE_MISS — браузер не нашёл нужной копии в кэше и просит отправить форму заново.
- Редирект 301 или 302 — соседние коды 3xx.
- Ошибка 400 — сервер не смог разобрать запрос.
- Скорость отдачи сервера — сколько времени уходит на ответ.
Границы замера
Стенд — nginx 1.22.1, Apache 2.4.68 и PHP 8.3 в Docker, Chromium 149. Обход 18.10.2026 из Германии, по одному обычному и одному-двум условным запросам на главную страницу; внутренние страницы и картинки мы не проверяли. Почему у сайтов за DDoS-Guard ETag меняется, мы не выясняли.
Коротко
Код 304 — не ошибка, а ответ «страница не изменилась, берите копию». Работает он через ETag и Last-Modified. Статику nginx и Apache отдают с 304 сами, PHP-страницы — нет. Проблемы две: 304 при изменённой странице показывает старую версию, а отсутствие 304 заставляет каждый раз скачивать всё целиком. Первое лечится жёсткой перезагрузкой у посетителя и правкой выкладки у владельца, второе — настройкой сервера.
