HTTPQUERY
Русский

Cache-Control

Применение

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

В таблице ниже показано, к чему применяется каждая директива: к запросам, ответам или к тому и другому.

Несколько директив разделяются запятыми в одном значении заголовка либо распределяются по нескольким заголовкам Cache-Control. При появлении конфликтующих директив применяется наиболее строгая комбинация.

Примечание

Нераспознанные директивы игнорируются кэшами. Это позволяет вводить новые директивы, не ломая старые реализации.

Примечание

Когда заголовок Cache-Control отсутствует, кэши применяют эвристическую свежесть. Распространённая эвристика — 10 % от времени, прошедшего с Last-Modified. Это означает, что ответы без Cache-Control кэшируются браузерами и CDN. Явная установка Cache-Control предотвращает неожиданное поведение кэширования.

Примечание

Cache-Control определяет, когда выполнять повторную валидацию (время жизни свежести). ETag определяет, как её выполнять (идентичность содержимого). Они работают вместе: Cache-Control решает, нужна ли повторная валидация, а ETag — изменилось ли содержимое. Заголовок Pragma — это эквивалент Cache-Control: no-cache из HTTP/1.0, фактически считающийся устаревшим.

max-age

Директива max-age указывает кэшам возвращать сохранённый ответ только в том случае, если его возраст не превышает заданного числа секунд. Некорректные значения, например отрицательные числа, трактуются как 0.

Cache-Control: max-age=<seconds>

Установка max-age=0 принудительно запускает сквозную повторную валидацию. Этот приём возник в реализациях HTTP/1.0, не поддерживавших no-cache.

max-stale

Директива max-stale сообщает о готовности принять ответ, у которого Age превысил время жизни свежести не более чем на указанное число секунд. Окно допуска начинается после истечения max-age.

Cache-Control: max-stale=<seconds>

Эта директива полезна, когда сервер-источник временно недоступен, а слегка устаревший ответ допустим.

min-fresh

Директива min-fresh запрашивает сохранённый ответ, у которого оставшееся время жизни свежести составляет не менее указанного числа секунд. Кэш возвращает сохранённую копию, только если ответ останется свежим в течение этого дополнительного периода.

Cache-Control: min-fresh=<seconds>

no-cache

Запрос с директивой no-cache требует, чтобы кэш перед выдачей копии проверил сохранённый ответ у источника. Это принудительно запускает повторную валидацию, не отбрасывая сохранённую запись.

Cache-Control: no-cache

Примечание

Чтобы полностью запретить кэшу сохранять ответ, используйте no-store.

no-store

Директива no-store просит кэши не сохранять ни запрос, ни соответствующий ответ.

Cache-Control: no-store

no-transform

Директива no-transform запрещает промежуточным узлам изменять тело ответа, например повторно сжимать изображения или преобразовывать форматы медиа. Ограничение действует независимо от того, является ли промежуточный узел кэширующим прокси или шлюзом-пересыльщиком.

Cache-Control: no-transform

only-if-cached

Директива only-if-cached указывает кэшу вернуть сохранённый ответ, не обращаясь к источнику. Если подходящего сохранённого ответа нет, кэш возвращает статус 504.

Cache-Control: only-if-cached

max-age

Директива max-age объявляет число секунд, в течение которых ответ остаётся свежим после генерации. Отсчёт начинается с момента создания ответа источником, поэтому время передачи и время, проведённое в промежуточных кэшах, вычитаются из этого бюджета.

Cache-Control: max-age=<seconds>

Ответ с max-age=3600 остаётся свежим один час. По истечении часа кэши считают ответ устаревшим и либо выполняют повторную валидацию, либо получают новую копию — в зависимости от других присутствующих директив.

s-maxage

Директива s-maxage (shared max-age) переопределяет max-age для разделяемых кэшей, таких как CDN и прокси-серверы. Частные браузерные кэши игнорируют s-maxage и возвращаются к max-age.

Cache-Control: s-maxage=<seconds>

no-cache

Директива no-cache в ответе требует, чтобы кэши выполняли повторную валидацию сохранённого ответа у источника перед каждым использованием. Кэш по-прежнему сохраняет ответ, что позволяет делать условные запросы с ETag или Last-Modified.

Cache-Control: no-cache

Когда no-cache включает список имён полей, повторной валидации требуют только эти конкретные заголовки. Остальная часть сохранённого ответа выдаётся без обращения к источнику.

Cache-Control: no-cache="Set-Cookie"

Примечание

Чтобы полностью запретить сохранение, используйте no-store.

no-store

Директива no-store запрещает любому кэшу сохранять ответ. Каждый последующий запрос уходит к источнику.

Cache-Control: no-store

Примечание

no-cache разрешает сохранение, но требует повторной валидации перед каждым использованием. Кэш сверяется с источником через If-None-Match или If-Modified-Since и получает 304, если содержимое не изменилось. no-store полностью запрещает сохранение, поэтому никакая копия не хранится и каждый запрос получает полный ответ. Распространённая ошибка — использовать no-cache там, где для конфиденциальных данных вроде банковских или медицинских записей подразумевался no-store. no-cache эффективен для часто обновляемого содержимого. no-store предназначен для содержимого, которое никогда не должно сохраняться на диск.

no-transform

Директива no-transform в ответе запрещает промежуточным узлам изменять тело ответа перед пересылкой независимо от того, кэширует ли узел содержимое.

Cache-Control: no-transform

must-revalidate

Директива must-revalidate разрешает кэшам выдавать ответ, пока он свеж. Как только ответ устарел, кэш обращается к источнику для повторной валидации, прежде чем выдать его снова. Если источник недоступен, кэш возвращает 504 вместо выдачи устаревшего содержимого.

Cache-Control: must-revalidate

proxy-revalidate

Директива proxy-revalidate работает так же, как must-revalidate, за исключением того, что требование применяется только к разделяемым кэшам. Частные браузерные кэши не затрагиваются.

Cache-Control: proxy-revalidate

must-understand

Директива must-understand предписывает кэшу сохранять ответ только тогда, когда кэш распознаёт код состояния и понимает связанные с ним требования к кэшированию.

Cache-Control: must-understand, no-store

Сочетание must-understand с no-store даёт запасной вариант: кэши без поддержки must-understand игнорируют неизвестную директиву и вместо этого соблюдают no-store.

private

Директива private ограничивает хранение частными кэшами, как правило браузером конечного пользователя. Разделяемые кэши, такие как CDN и прокси-серверы, отбрасывают ответ.

Cache-Control: private

public

Директива public помечает ответ как пригодный для хранения в разделяемых кэшах. Ответы с заголовком Authorization не сохраняются разделяемыми кэшами, если не присутствует директива public.

Cache-Control: public

Примечание

Директива public не нужна, когда уже присутствует must-revalidate или s-maxage.

immutable

Директива immutable гарантирует, что тело ответа не изменится в течение времени жизни свежести. Кэши пропускают условные запросы для неизменяемых ресурсов, устраняя обращения на повторную валидацию для таких ресурсов, как версионированные JavaScript-бандлы или изображения с отпечатком в имени.

Cache-Control: public, max-age=31536000, immutable

stale-while-revalidate

Директива stale-while-revalidate продлевает окно пригодности устаревшего ответа. После истечения свежести кэши выдают устаревшую копию, параллельно выполняя повторную валидацию в фоне. Параметр задаёт, сколько дополнительных секунд устаревший ответ остаётся приемлемым.

Cache-Control: max-age=600, stale-while-revalidate=30

Как только фоновая повторная валидация завершается, кэш заменяет устаревшую запись свежим ответом. Этот приём скрывает задержку повторной валидации от конечных пользователей.

stale-if-error

Директива stale-if-error позволяет кэшам выдавать устаревший ответ, когда источник возвращает ошибочный статус (500, 502, 503 или 504) либо недоступен. Параметр задаёт число секунд сверх времени жизни свежести, в течение которых устаревший ответ остаётся пригодным в качестве запасного варианта.

Cache-Control: max-age=600, stale-if-error=86400

Пример

Статический ресурс, выдаваемый с длительным временем жизни свежести и флагом immutable. Браузер и любой разделяемый кэш хранят этот ответ год без повторной валидации.

Cache-Control: public, max-age=31536000, immutable

Ответ API, предназначенный только для запросившего его браузера. Кэш хранит ответ пять минут и выполняет повторную валидацию перед повторным использованием, когда ответ устарел.

Cache-Control: private, max-age=300, must-revalidate

Политика, нацеленная на CDN: разделяемому кэшу даётся десятиминутное окно, а браузерному — одна минута. Устаревшие ответы выдаются до 30 секунд, пока CDN выполняет повторную валидацию в фоне.

Cache-Control: s-maxage=600, max-age=60, stale-while-revalidate=30

Googlebot и max-age

Инфраструктура сканирования Google реализует эвристическое HTTP-кэширование. Директива max-age помогает Googlebot определить частоту повторного сканирования. Страница с большим max-age запрашивается заново реже, тогда как no-cache сигнализирует о частых изменениях содержимого и оправдывает более частое сканирование. Для некоторых кодов состояния ответы без каких-либо заголовков кэширования по умолчанию кэшируются эвристически, поэтому источникам, желающим полностью исключить кэширование, нужно явно отправлять no-store. Bingbot отправляет в запросах Cache-Control: no-cache и для регулирования темпа сканирования опирается на crawl-delay в robots.txt и IndexNow.

Примечание

За помощью по SEO и кэшированию обращайтесь к бывшим SEO-консультантам Google Search Brothers.

Диагностика

Неожиданное поведение кэширования возникает из-за неверного понимания директив, переопределений на стороне CDN или конфликтующих заголовков.

Устаревшее содержимое выдаётся несмотря на директиву no-cache. Директива no-cache не запрещает сохранение. Кэши сохраняют ответ и выполняют повторную валидацию при каждом использовании. Чтобы полностью запретить сохранение, используйте no-store. Когда речь идёт о конфиденциальных данных, комбинируйте обе: Cache-Control: no-cache, no-store.

CDN игнорирует директивы Cache-Control. Многие CDN переопределяют Cache-Control источника собственными политиками. Cloudflare по умолчанию соблюдает заголовки источника, но переопределяет их при активном Page Rule или Cache Rule. AWS CloudFront использует заголовок источника, если не подключена политика кэширования с собственными TTL. Прежде чем отлаживать конфигурацию источника, проверьте правила переопределения в панели управления CDN.

Устаревшее содержимое сохраняется после снижения max-age. Пограничные узлы CDN и браузерные кэши удерживают ответы до истечения исходного max-age. Снижение значения на источнике не очищает уже закэшированные копии. После изменения времени жизни кэша выполните очистку CDN. В Cloudflare очищайте по URL или выполните полную очистку зоны. В CloudFront создайте инвалидацию для затронутых путей.

Браузер показывает старую страницу при навигации назад/вперёд несмотря на no-store. Кэш навигации назад/вперёд (bfcache) в Chrome, Firefox и Safari восстанавливает полное состояние страницы из памяти в обход no-store. Это поведение задумано намеренно и не является нарушением правил кэширования. Проверьте заголовок Cache-Control во вкладке Network в DevTools со снятым флажком “Disable cache”, чтобы убедиться, что заголовок доходит до браузера. Добавление обработчиков события unload отключает bfcache в некоторых браузерах, но ухудшает производительность.

Заголовок Expires конфликтует с Cache-Control. Когда присутствуют оба заголовка, Cache-Control: max-age имеет приоритет над Expires. Удалите заголовок Expires, чтобы избежать путаницы. В nginx:

expires off;
add_header Cache-Control "max-age=3600";

В Apache:

Header unset Expires
Header set Cache-Control "max-age=3600"

Диагностика заголовков кэширования с помощью curl и DevTools. Выполните curl -I https://example.re, чтобы изучить заголовки ответа от источника. Добавьте -H “Cache-Control: no-cache”, чтобы обойти кэши CDN и обратиться напрямую к источнику. В DevTools браузера откройте вкладку Network, выберите запрос и просмотрите раздел Response Headers. Обратите внимание на заголовки Age, X-Cache и CF-Cache-Status, чтобы определить, пришёл ли ответ с пограничного узла CDN или от источника.