Откройте вкладку Network в браузере и перезагрузите любую страницу второй раз — среди привычных 200 обязательно мелькнёт несколько строк со статусом 304. Клиенты периодически спрашивают, не ошибка ли это. Нет, не ошибка. Это один из самых полезных механизмов HTTP, о котором большинство владельцев сайтов даже не задумывается — пока не начинает разбираться, почему сайт грузится медленно при повторных заходах.
Что происходит за кодом 304
При первом обращении к файлу — стилю, скрипту, картинке — сервер отдаёт его целиком с кодом 200 OK. Браузер не выбрасывает эту копию сразу после использования, а кладёт в кеш и запоминает пару деталей: дату последнего изменения файла и что-то вроде отпечатка его содержимого.
При следующем визите браузер не просит файл заново. Он спрашивает иначе: «у меня уже есть эта версия, вот её отпечаток — она ещё актуальна?» Если сервер подтверждает, что файл не поменялся, в ответ приходит 304 Not Modified — без единого байта самого файла, только служебные заголовки. Дальше браузер просто достаёт файл из собственного кеша.
ETag, Last-Modified, Cache-Control — путаница на пустом месте
Разобраться стоит, потому что три заголовка делают похожие, но не одинаковые вещи.
- Cache-Control задаёт время, в течение которого браузер вообще не будет спрашивать сервер: значение вроде
max-age=86400означает «сутки бери из кеша без проверки». Пока этот срок не истёк, никакого запроса с 304 не будет — файл отдаётся мгновенно, вообще без обращения к серверу. - Last-Modified — дата последнего изменения файла. Браузер возвращает её серверу в заголовке If-Modified-Since после истечения max-age, когда пора проверить актуальность.
- ETag — что-то вроде хеша содержимого, точнее даты. Два файла на диске могут иметь одинаковую дату изменения после деплоя через CI/CD, но разное содержимое — ETag это ловит, дата иногда нет.
Вместе эти заголовки образуют ту самую проверку «актуален ли файл ещё», итогом которой становится 304.

Почему это реально ускоряет сайт
Возьмём каталог интернет-магазина. Главная страница тянет за собой стили, несколько скриптов, иконки, логотип — набегает пара мегабайт легко. Первый визит всегда самый тяжёлый, всё это грузится с нуля. А вот второй, третий, десятый заход — совсем другая история, если кеширование настроено правильно.
Покупатель вернулся через час посмотреть другой товар. У браузера уже есть стили и скрипты в кеше, и вместо повторной загрузки пары мегабайт идёт несколько коротких запросов с ответом 304, каждый на пару сотен байт. На медленном мобильном интернете где-нибудь в дороге разница ощущается физически: страница, которая должна была грузиться пять секунд, открывается почти мгновенно.
Есть и второй эффект, не такой очевидный. Google учитывает скорость повторных загрузок при оценке Core Web Vitals для мобильных пользователей, и сайт с плохо настроенным кешированием статики проигрывает в этой метрике, даже если первая отрисовка быстрая.
Как проверить, работает ли это вообще
Проще всего открыть DevTools (F12), перейти на вкладку Network, снять галочку Disable cache (по умолчанию при открытых DevTools она часто стоит, и тогда кеширование искусственно отключено), и перезагрузить страницу дважды.
Первая загрузка покажет в основном 200. Вторая — если кеширование настроено — должна показать 304 напротив стилей, скриптов, шрифтов, иконок. В колонке Size часто прямо написано «(from disk cache)» или «(memory cache)» для файлов, которые вообще не дошли до сервера, и отдельно 304 для тех, что дошли и получили подтверждение.
Если после второй загрузки всё так же 200 с полным размером файлов — кеширование не работает, и стоит разбираться, почему.
Что чаще всего ломает 304 на реальных проектах
Причин обычно несколько, и они редко связаны друг с другом.
Сервер вообще не отправляет заголовки кеширования. Встречается на дешёвом хостинге или когда Apache или Nginx настроен «по умолчанию» без модуля expires и явных правил для статики. Файлы всегда идут с кодом 200, даже если не менялись годами.
CDN и плагин кеширования конфликтуют между собой. Один слой добавляет свои заголовки Cache-Control, другой их перезаписывает своими — на выходе браузер получает противоречивые инструкции. Встречались случаи, когда CDN принудительно ставил короткий max-age поверх более длинного серверного значения, и кеширование фактически сводилось к нулю.
Слишком агрессивный cache-busting. Сама идея полезная — добавлять версию к имени файла (style.css?v=123), чтобы браузер точно подхватил новую версию после обновления. Но если версия генерируется от времени сборки или случайного числа при каждом запросе страницы, а не от реального содержимого файла, браузер каждый раз видит «новый» файл, и кеширование ломается именно теми инструментами, которые должны были его ускорять.
Плагины оптимизации в WordPress сами иногда виноваты: если плагин минификации CSS или JS генерирует новое имя файла при каждой пересборке кеша страницы, эффект тот же — старый кеш браузера просто никогда не используется.
Чем это отличается от кеш-плагинов вроде WP Rocket или W3 Total Cache
Тут обычно и возникает путаница, потому что оба механизма называют «кешированием», но работают на разных уровнях.
Плагин полного кеширования страниц хранит готовый HTML на сервере и отдаёт его следующему посетителю без повторного обращения к базе данных и PHP — это ускоряет саму генерацию страницы. 304 работает совсем в другом месте: он говорит браузеру не загружать повторно статические файлы, которые у него и так уже есть на диске. Одно экономит время сервера, другое — время и трафик на стороне клиента.
Лучший результат — когда оба механизма работают вместе. Плагин кеширования ускоряет отдачу самой HTML-страницы, а правильные заголовки кеширования ускоряют всё, что эта страница подгружает. Без второго первое всё равно оставляет браузер каждый раз тянуть заново пару мегабайт стилей и скриптов.

Что с этим делать
Если проверка в DevTools показала сплошные 200 вместо ожидаемых 304 — начните с заголовков Cache-Control на сервере для статики (CSS, JS, изображения, шрифты), затем проверьте, не перезаписывает ли их незаметно CDN или плагин оптимизации. Это не та задача, которую стоит решать наугад — один неправильный заголовок может либо вообще отключить кеширование, либо, наоборот, заставить браузер показывать устаревшую версию файла после обновления сайта, а это уже другая, более неприятная проблема.