304 — это не сбой
Многие впервые видят код 304 в инструментах разработчика и принимают его за проблему. На самом деле это штатный и полезный ответ. Он означает: содержимое с прошлого раза не изменилось, можно использовать копию из кеша, тело ответа сервер повторно отправлять не будет. Благодаря этому страницы открываются быстрее, а трафик расходуется меньше.
Механизм называется условным запросом. Браузер, у которого уже есть копия, спрашивает сервер: изменилась ли она с такого-то момента или с такой-то версии? Если нет, приходит короткий ответ 304 без тела. Если да, сервер присылает свежий ответ с кодом 200.
Как это работает: ETag и Last-Modified
Сервер вместе с ресурсом отдаёт признак его версии. Один из них — Last-Modified, дата последнего изменения. Другой — ETag, метка, которая меняется, когда меняется содержимое. При повторном обращении браузер отправляет эти значения обратно в заголовках If-Modified-Since и If-None-Match.
Сервер сравнивает присланное с текущим состоянием. Если совпадает, отвечает 304 и заголовками для обновления срока годности кеша. Отдельно действует Cache-Control: пока срок годности не истёк, браузер вовсе не обращается к серверу, а использует копию сразу. Условный запрос происходит, когда срок истёк или пользователь нажал обновление.
Когда 304 становится проблемой
Проблемы возникают, если сервер неверно определяет, что содержимое не менялось. Например, страница формируется динамически, но метка версии не учитывает изменения в данных, и посетитель продолжает видеть старую версию. Другой сценарий — прокси или сеть доставки хранит копию дольше, чем нужно.
Со стороны пользователя типичная жалоба звучит так: «я обновил сайт, а на странице по-прежнему всё по-старому». Причина может быть в локальном кеше, в промежуточном кеше или в неверных заголовках. Бывает и обратная ситуация: сервер вообще не поддерживает условные запросы и каждый раз отдаёт полный ответ, из-за чего страницы грузятся дольше, чем могли бы.
Как проверить: HTTP 304 Not Modified
Откройте инструменты разработчика, вкладку сети, и включите отображение заголовков. Посмотрите, какие If-None-Match и If-Modified-Since отправил браузер, какие ETag и Last-Modified вернул сервер и какой пришёл код. Полезно сравнить обычную загрузку и жёсткое обновление, при котором браузер игнорирует кеш.
Чтобы убедиться, что страница действительно свежая, откройте её в приватном окне: там нет прежних копий. Если в приватном окне содержимое обновлённое, а в обычном старое, виноват локальный кеш, и его можно очистить для конкретного сайта.
Почему 304 иногда «не срабатывает»
Если в инструментах разработчика вы видите, что все ресурсы каждый раз загружаются полностью с кодом 200, значит, условные запросы не используются. Причины обычно такие: сервер не отдаёт ETag и Last-Modified, включён запрет кеширования, ресурс формируется скриптом без учёта заголовков или к адресу добавляется случайный параметр, из-за чего каждый запрос считается новым.
Бывает и обратное: пользователь жмёт обычное обновление и удивляется, почему получил 304 вместо свежих данных. Но 304 честно означает, что данные не менялись, и увидеть изменения можно, только если они действительно произошли на сервере. Если изменения были, а ответ остаётся 304, проверьте метки версии: возможно, сервер пересчитывает их некорректно и после правки продолжает выдавать прежнее значение. Для проверки достаточно сравнить значения ETag до и после изменения содержимого.
Настройка для владельца сайта
Для статических файлов включайте ETag или Last-Modified, задавайте разумный Cache-Control и добавляйте к именам файлов версию или хеш содержимого: тогда можно ставить долгий срок годности, а при обновлении имя меняется. Для динамических страниц следите, чтобы метка версии зависела от реальных данных, иначе пользователи получат устаревшее содержимое.
Не используйте одновременно противоречивые заголовки, например запрет кеширования вместе с долгим сроком годности. Проверьте, что сеть доставки и обратный прокси корректно передают заголовки условных запросов и не подменяют их.
Проверяйте настройки не только на самом сервере, но и на всех промежуточных узлах: сеть доставки способна убирать или переписывать заголовки. После каждого изменения открывайте страницу дважды и сравнивайте ответы: первая загрузка должна вернуть код 200, повторная — 304 либо обращение к кешу без запроса. Это простой способ убедиться, что вся цепочка работает так, как задумано.