Как проверить HTTP-кэш через curl: ревалидация и устаревший ответ

Разбираем, как curl проверяет ревалидацию HTTP-кэша, что означают 304, ETag и Age и как отличить устаревший ответ от кэша браузера.

Проверить ревалидацию HTTP-кэша через curl можно условным GET-запросом: сначала сохраните ETag или Last-Modified, затем отправьте его в If-None-Match или If-Modified-Since. Ответ 304 Not Modified означает, что сервер или промежуточный кэш подтвердил актуальность сохранённой версии; 200 OK возвращает представление заново.

Сам curl не читает кэш браузера. Каждый запуск делает отдельный запрос, который может пройти через CDN, reverse proxy или другой общий кэш. Поэтому задача проверки — сопоставить тело, валидаторы и заголовки кэширования на одном и том же публичном URL.

Сначала зафиксируйте обычный ответ

Используйте GET, а не только curl -I: обработка HEAD иногда отличается от обработки реальной загрузки. Сохраните заголовки отдельно от тела:

curl -sS -L --max-redirs 5 --compressed \
  -D headers-1.txt \
  -o body-1.bin \
  -w 'code=%{http_code} final=%{url_effective} bytes=%{size_download}\n' \
  https://example.com/assets/app.css

Если были редиректы, в файле окажется несколько блоков заголовков. Анализируйте последний ответ и зафиксируйте итоговый URL. Для сравнения особенно полезны:

  • Cache-Control и Expires — правила свежести;
  • Age — возраст ответа в кэше, если заголовок передаётся;
  • ETag и Last-Modified — валидаторы представления;
  • Vary — заголовки запроса, участвующие в выборе варианта ответа;
  • Cache-Status — стандартизированная диагностика кэша, если она включена;
  • X-Cache и похожие поля — дополнительные подсказки конкретного CDN, но их значения зависят от провайдера.

Сохраните контрольную сумму тела командой sha256sum body-1.bin или shasum -a 256 body-1.bin в macOS. Хеш помогает понять, совпадают ли байты двух полных ответов, но сам по себе не объясняет, почему был использован кэш.

Как проверить ревалидацию по ETag

Если установленный curl поддерживает работу с файлами ETag, валидатор можно сохранить и применить без ручного копирования:

curl -sS --compressed \
  --etag-save etag.txt \
  -D headers-1.txt \
  -o body-1.bin \
  https://example.com/assets/app.css

curl -sS --compressed \
  --etag-compare etag.txt \
  --etag-save etag.txt \
  -D headers-2.txt \
  -o body-2.bin \
  -w 'code=%{http_code}\n' \
  https://example.com/assets/app.css

Второй запрос отправляет If-None-Match со значением из файла. Возможны два основных результата:

  1. 304 Not Modified: представление не изменилось согласно валидатору. Тела у такого ответа нет — клиент должен использовать ранее сохранённое.
  2. 200 OK: сервер вернул полное тело. Сравните новый ETag, заголовки и хеш; ресурс мог измениться, а мог не поддерживать условную обработку ожидаемым образом.

Если ETag отсутствует, скопируйте точное значение Last-Modified из первого ответа:

curl -sS -D headers-2.txt -o body-2.bin \
  -H 'If-Modified-Since: <значение Last-Modified>' \
  https://example.com/assets/app.css

Когда одновременно доступны оба валидатора, проверка через If-None-Match приоритетнее. Не придумывайте дату и не подставляйте время запуска: условие должно ссылаться на реально полученную версию ресурса.

«Обновить» и «обойти кэш» — разные проверки

Заголовок запроса Cache-Control: no-cache просит кэши проверить сохранённый ответ перед использованием. Он не удаляет объект и не означает «никогда не хранить»:

curl -sS --compressed \
  -H 'Cache-Control: no-cache' \
  -D headers-revalidated.txt \
  -o body-revalidated.bin \
  https://example.com/assets/app.css

Сравните этот результат с обычным GET на том же URL. Если добавить случайный query-параметр, например ?test=..., получится другой ключ кэша во многих конфигурациях. Такой запрос может показать свежий файл, но не докажет, что исправлен исходный URL без параметра. Кроме того, отдельные CDN умеют игнорировать query string при построении ключа.

Не начинайте с очистки кэша: сначала сохраните проблемный ответ, время, итоговый URL и заголовки. Иначе исчезнет часть данных, по которым можно отличить неверный TTL, ошибочный ключ или неудачную ревалидацию от незавершённого развёртывания.

Как распознать устаревший ответ после изменения

Проверяйте на контролируемом публичном ресурсе, где вы точно знаете ожидаемую версию.

  1. До изменения сохраните тело, хеш, ETag, Last-Modified, Cache-Control, Age и Vary.
  2. Опубликуйте безопасный различимый маркер версии — например, комментарий в тестовом CSS или текст на диагностической странице.
  3. Повторите обычный GET по прежнему URL с теми же существенными заголовками запроса.
  4. Выполните условный GET со старым валидатором.
  5. Сопоставьте результат с настройками свежести и журналом развёртывания. При необходимости повторите проверку из другой разрешённой сети или точки наблюдения.

| Наблюдение | Что это означает | Что проверить дальше | |---|---|---| | 304 на старый ETag | Узел считает сохранённое представление актуальным | Должен ли валидатор был измениться после публикации | | 200, новое тело и новый валидатор | Обновление дошло по проверяемому маршруту | Совпадает ли результат в остальных нужных точках | | 200, но осталось старое тело | Причина ещё не доказана | Итоговый URL, факт развёртывания, cache key, Vary, правила CDN | | Растёт Age, а тело не меняется | Промежуточный кэш повторно использует объект | max-age, s-maxage, разрешение stale-ответов и состояние origin | | curl видит новое, браузер — старое | Серверный маршрут отвечает актуально | Disk/memory cache, service worker и расширения браузера | | Разные ответы при разных заголовках | Вероятны разные варианты кэша | Vary, Accept-Encoding, авторизация и cookies |

Один X-Cache: HIT не доказывает ошибку, а MISS не гарантирует свежесть: поставщики определяют эти поля по-разному. Также старый ответ может быть разрешён директивами вроде stale-if-error, если origin временно недоступен. Оценивать его нужно вместе с правилами ответа и фактическим состоянием origin.

Когда curl недостаточно

curl не воспроизводит memory cache, disk cache, service worker, JavaScript и полный набор браузерных заголовков. Если проблема видна только посетителю, сравните запрос во вкладке Network с curl: точный URL, метод, редиректы, Vary, Accept-Encoding и наличие авторизации.

Не копируйте в команду cookies, токены и закрытые ссылки, если вывод попадёт в тикет или общий лог. Для публичного CDN-ресурса пригодится отдельная инструкция о проверке файла через браузер и curl.

Что делать после исправления

Разовый curl отвечает только за конкретный маршрут и момент. Для важной публичной страницы или ресурса можно настроить в Web-Puls регулярную проверку доступности и ожидаемого содержимого: так повторное появление старой версии или ошибочного ответа будет видно без ручного запуска команд.

Добавьте критичный публичный URL в мониторинг после того, как определили ожидаемый HTTP-код и маркер содержимого. Для browser-only ошибок оставьте отдельную проверку пользовательского сценария — обычный HTTP-запрос её не заменяет.

Источники

Проверьте свой сайт прямо сейчас

Введите адрес сайта: Web-Puls покажет HTTP-код, время ответа и базовую диагностику. Для постоянного контроля можно подключить мониторинг.

Хотите видеть такую историю по своему сайту?

Добавьте сайт в Web-Puls: мы будем проверять доступность, SSL и содержимое страницы.