HTTPQUERY
Русский

Vary

Применение

Один и тот же URL часто порождает разные ответы в зависимости от того, что отправляет клиент. Сервер, сжимающий ответ с помощью gzip для одного клиента и Brotli для другого, отдаёт два разных представления одного ресурса. Заголовок Vary фиксирует, какие заголовки запроса заставили сервер выбрать конкретное представление, давая кэшам информацию, необходимую для сохранения и сопоставления нужной копии.

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

Самые распространённые значения — Accept-Encoding (для согласования сжатия) и Accept (для согласования содержимого). Конфигурации динамической отдачи, которые выдают разный HTML мобильным и настольным клиентам на основе строки User-Agent, добавляют Vary: User-Agent, чтобы обозначить эту вариативность.

Подстановочное значение * означает, что каждый запрос считается уникальным, и фактически отключает кэширование ресурса. Это редко бывает уместно, и большинство кэшей считают ответ с Vary: * некэшируемым.

Примечание

Google не использует заголовок Vary для понимания связи между мобильной и настольной версиями при поисковой индексации. Этот заголовок — механизм кэширования, а не сигнал для поиска. Сайты с динамической отдачей и Vary: User-Agent по-прежнему выигрывают от корректного поведения кэша, но сам заголовок никак не влияет на то, как Google обнаруживает или индексирует мобильный контент. Google также не рекомендует делать m-dot URL канонической версией, несмотря на индексирование с приоритетом мобильного контента, и предлагает адаптивный дизайн как предпочтительную конфигурацию.

Accept-Encoding

Указывает, что ответ меняется в зависимости от заголовка запроса Accept-Encoding. Разные алгоритмы сжатия (gzip, br, zstd) дают разные тела ответа для одного и того же ресурса. Это наиболее широко используемое значение Vary.

Accept

Указывает, что ответ меняется в зависимости от заголовка запроса Accept. Согласование содержимого отдаёт разные медиатипы (HTML, JSON, XML) с одного URL в соответствии с предпочтениями клиента.

Accept-Language

Указывает, что ответ меняется в зависимости от заголовка запроса Accept-Language. Это значение включают многоязычные сайты, отдающие локализованный контент с одного URL.

User-Agent

Указывает, что ответ меняется в зависимости от заголовка запроса User-Agent. Конфигурации динамической отдачи выдают разный HTML мобильным и настольным клиентам с одного URL.

Vary: Cookie означает, что ответ различается в зависимости от значения заголовка Cookie. Кэши сохраняют отдельный вариант для каждой уникальной строки Cookie, что вызывает значительную фрагментацию кэша, поскольку у каждого аутентифицированного пользователя свой сессионный cookie. Ограничьте Vary: Cookie теми ответами, которые действительно зависят от значений cookie (персонализированные страницы, панели управления), и не устанавливайте это значение для публичных статических ресурсов.

* (подстановочный знак)

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

Пример

Сервер сжимает ответы тем кодированием, которое запросил клиент. Заголовок Vary: Accept-Encoding указывает промежуточным кэшам хранить отдельные копии для запросов с gzip и Brotli, а не отдавать сжатый gzip ответ клиенту, поддерживающему Brotli.

Vary: Accept-Encoding

Конфигурация с согласованием содержимого отдаёт JSON или HTML с одной и той же конечной точки. Кэш хранит отдельные ответы в зависимости от значения заголовка Accept.

Vary: Accept

Несколько заголовков, влияющих на ответ, перечисляются через запятую. Здесь ответ зависит и от принимаемого кодирования, и от предпочтительного языка.

Vary: Accept-Encoding, Accept-Language

Конфигурация динамической отдачи выдаёт разный HTML мобильным и настольным агентам пользователя. Кэш хранит отдельные версии с ключом по полной строке User-Agent.

Vary: User-Agent

Сочетание Vary с Cache-Control позволяет управлять и тем, что варьируется, и тем, как долго каждый вариант остаётся свежим.

Cache-Control: public, max-age=3600
Vary: Accept-Encoding

Диагностика

Проблемы кэширования, вызванные отсутствующим или слишком широким заголовком Vary, часто проявляются в том, что пользователи получают не тот контент.

CDN отдаёт неправильный кэшированный вариант. Сервер возвращает разное содержимое в зависимости от заголовка запроса, но не указывает соответствующее значение Vary. CDN кэширует первый ответ и отдаёт ту же копию всем клиентам независимо от их заголовков запроса. Добавьте имя нужного заголовка в список Vary. Для проверки отправьте через curl запросы с разными значениями заголовка и сравните заголовки ответа X-Cache или Age: curl -H “Accept-Encoding: gzip” -v https://cdn.example.re/style.css curl -H “Accept-Encoding: br” -v https://cdn.example.re/style.css

Фрагментация кэша из-за Vary: User-Agent. Строка User-Agent имеет тысячи уникальных значений. Указание User-Agent в заголовке Vary создаёт отдельную запись кэша практически для каждого посетителя, уничтожая долю попаданий в кэш. CDN вроде Cloudflare и Fastly нормализуют User-Agent до классов устройств. Там, где это возможно, используйте нормализованный заголовок, например Sec-CH-UA-Mobile, или специфичный для CDN заголовок типа устройства вместо исходного User-Agent.

Vary: * полностью отключает кэширование. Подстановочный знак сообщает кэшам, что ответ зависит от факторов вне любого заголовка запроса. Большинство кэшей считают такой ответ некэшируемым и никогда не отдают сохранённую копию. Удалите Vary: *, если только намерение не состоит в том, чтобы отключить всё кэширование ресурса. Если вариативность определяется конкретным заголовком запроса, укажите этот заголовок явно.

Отсутствие Vary: Origin приводит к отравлению CORS-кэша. Сервер возвращает динамическое значение Access-Control-Allow-Origin на основе заголовка запроса Origin, но не включает Vary: Origin. CDN кэширует ответ для одного источника и отдаёт тот же CORS-заголовок другому источнику, из-за чего браузеры блокируют ответ. Добавляйте Vary: Origin к каждому ответу, который отражает динамическое значение источника. Для диагностики отправьте два запроса curl с разных источников и проверьте, меняется ли кэшированный Access-Control-Allow-Origin: curl -H “Origin: https://a.example.re” -v https://api.example.re/data curl -H “Origin: https://b.example.re” -v https://api.example.re/data

Несогласованность Vary: Accept-Encoding между gzip и Brotli. Некоторые серверы-источники сжимают с помощью gzip, а граничный узел CDN пережимает в Brotli, из-за чего кэшированное кодирование не совпадает с ключом Vary. Убедитесь, что источник и CDN согласованы в поведении кодирования. В nginx задайте gzip_vary on;, чтобы заголовок Vary: Accept-Encoding добавлялся автоматически. При использовании Brotli наряду с gzip убедитесь, что оба модуля добавляют одно и то же значение Vary, чтобы кэш хранил отдельные записи для каждого кодирования.