Файл на сервере актуальный, в браузере — старый. Жёсткое обновление помогает, но в приватном окне снова старая страница. Проблема не в кэше браузера, а в слое между вами — и решается она не кнопкой очистки при каждом деплое.
Cloudflare по умолчанию кэширует статические ассеты (CSS, JS, изображения, шрифты) и не кэширует HTML. Если ваш HTML всё же кэшируется, значит либо правило говорит кэшировать всё, либо сервер отдаёт для HTML длинный Cache-Control. Правильное решение — разделить два слоя: версионируйте статические ассеты в имени файла или строке запроса, чтобы очистка вообще не требовалась, ведь новая версия — это новый адрес. HTML держите короткоживущим и после деплоя чистите только изменившиеся адреса. Для диагностики читайте заголовок cf-cache-status: HIT — из кэша, MISS — с сервера, DYNAMIC — не кэшируется вовсе.
У жалобы «показывается старая страница» есть минимум три причины: кэш браузера, кэш CDN или собственный кэш сервера. Вместо догадок спросите заголовки:
curl -sSI https://example.com/ | grep -iE 'cf-cache-status|cache-control|age|last-modified'
Что читать:
cf-cache-status: HIT — ответ пришёл из кэша CDN, до вашего сервера дело не дошлоcf-cache-status: MISS — в кэше не было, взято с сервераcf-cache-status: DYNAMIC — этот адрес не кэшируется вовсеage: 8412 — сколько секунд ответ лежит в кэшеHIT с большим age на HTML означает, что причина найдена. DYNAMIC означает, что CDN ни при чём и смотреть надо на сервер.
На статическом сайте есть два типа содержимого, и их потребности в кэшировании противоположны.
Ассеты (CSS, JS, изображения, шрифты) меняются редко, а когда меняются — меняются целиком. Им следует оставаться в кэше как можно дольше.
HTML меняется часто и должен становиться видимым сразу. Его либо не кэшируют вовсе, либо держат очень недолго.
Большинство проблем развёртывания возникает от применения одной политики к обоим. «Кэшировать всё» делает сайт быстрым, а каждое обновление невидимым; «не кэшировать ничего» лишает CDN смысла.
Для ассетов правильный ответ — не очистка, а смена адреса. Если адрес меняется вместе с содержимым, старая копия в кэше перестаёт иметь значение: её никто не запрашивает.
<link rel="stylesheet" href="/styles.min.css?v=20260918" />
<script src="/script.min.js?v=20260918" defer></script>
Ещё надёжнее выводить строку версии из хеша содержимого файла — тогда забыть её обновить становится невозможно. Каким бы методом вы ни пользовались, правило одно: содержимое ассета не должно меняться без смены его адреса.
Как только это соблюдается, годовой срок кэширования ассетов полностью безопасен, и очистка после деплоя для них не нужна.
Единственное слабое место такой схемы — забытое обновление версии. Обновите CSS, не сменив версию, и посетители получат новый HTML со старыми стилями, то есть сломанную вёрстку — и обычная очистка кэша это не исправит, потому что файл отдаёт собственный кэш браузера.
Для HTML есть два разумных варианта. Первый — не кэшировать вовсе, оставив CDN слоем терминации TLS и защиты. Второй — короткий срок жизни, несколько минут, вместе с перепроверкой.
На стороне сервера короткий срок выглядит так:
Cache-Control: public, max-age=300, must-revalidate
Этот заголовок говорит одно и то же и браузеру, и CDN, и не позднее чем через пять минут после деплоя все видят новое содержимое. Если страницы уже отдают ETag и Last-Modified, проверка по истечении срока обходится дёшево: неизменившееся тело не скачивается заново.
При версионированных ассетах очистка нужна только для HTML — и должна быть последним шагом скрипта развёртывания, а не тем, что вы вспоминаете сделать руками.
# очистить только изменившиеся адреса
curl -sS -X POST \
"https://api.cloudflare.com/client/v4/zones/$ZONE/purge_cache" \
-H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
--data '{"files":["https://example.com/","https://example.com/services/"]}'
Очистка всего (purge_everything) проста, но это грубый инструмент: из кэша вылетают и все ассеты, следующие посетители скачивают заново всё подряд, а ваш сервер получает лишнюю нагрузку. На маленьком сайте вы этого не заметите, на нагруженном — заметите.
После очистки проверьте результат — снова, не предполагайте:
curl -sSI https://example.com/ | grep -i cf-cache-status # ожидаем MISS
Режим разработки. Режим разработки Cloudflare временно отключает кэширование и сам выключается через несколько часов. Полезен при отладке; оставленный включённым, становится частой причиной жалобы «сайт стал медленным».
Ответы 404 тоже кэшируются. Если страница была развёрнута не полностью и вернула 404, этот 404 попадает в кэш. Загрузить файл позже недостаточно — адрес продолжит отдавать 404, пока вы не очистите именно его.
Порядок имеет значение. Сначала загрузка, потом очистка. В обратном порядке CDN заново закэширует старое содержимое, и будет казаться, что ничего не изменилось.
Заголовки сервера управляют CDN. Если вы отдаёте для HTML длинный Cache-Control, браузеры будут держать файл у себя независимо от правил CDN — а очистка кэша CDN браузерный кэш не трогает.
Увидеть старое содержимое после деплоя — это не сбой кэша, а отсутствие политики. Версионируйте ассеты и кэшируйте их надолго; HTML держите короткоживущим и делайте точечную очистку последним шагом скрипта развёртывания. Как только эти два пункта на месте, весь класс ошибок «забыл почистить кэш» исчезает — а чтобы понять, что происходит, всегда достаточно одного заголовка: cf-cache-status.
cf-cache-status: HIT из кэша, MISS с сервера, DYNAMIC не кэшируется?v= или хеш содержимого) — очистка не нужнаmax-age=300, must-revalidate либо не кэшировать вовсе{"files":[...]}; purge_everything — грубый инструментПерестаньте гадать и прочитайте заголовки: curl -sSI и посмотрите cf-cache-status, age и cache-control. HIT с большим age означает, что ответ пришёл из кэша CDN. DYNAMIC означает, что CDN ни при чём и проблема на сервере или в браузере. Одна команда разделяет три возможности.
Это работает, но инструмент грубый. Из кэша вылетают и все ассеты, поэтому следующие посетители заново скачивают всё, от CSS до изображений, а ваш сервер получает лишнюю нагрузку. Если ассеты версионированы, их вообще не нужно чистить; очищайте только изменившиеся адреса HTML.
Потому что оно убирает состояние гонки. Когда версионированный адрес меняется, новый файл — это новый адрес; старую копию в кэше никто не запрашивает, и очищать её не требуется. Именно это делает безопасным годовой срок кэширования и сводит развёртывание к одной переменной: не забыть поднять версию.
Ответы 404 тоже кэшируются. Если не до конца развёрнутый адрес однажды вернул 404, загрузить файл позже недостаточно — нужно очистить именно этот адрес. Проверяйте после деплоя не только главную страницу, но и новые адреса.