Содержание статьи +
- TL;DR
- Зачем это знать
- Что такое cache key, аккуратно
- Дефолтный ключ кэша у каждого крупного CDN
- Vary header – «половинный близнец» cache key в спецификации
- Семь ошибок cache key, которые ломают стриминг
- Арифметика: один токен в query string, в десять раз счёт
- Пре-лонч-чек-лист по cache key
- Где Фора Софт вписывается
- Шпаргалка по вендорам, 2026
- Ключевые выводы
- Что читать дальше
TL;DR
Cache key – это короткий отпечаток, который Content Delivery Network (CDN) формирует из каждого входящего запроса и использует в качестве идентификатора кэшированного ответа. Проще говоря, это «имя файла» в кэше, и каждая деталь, которую CDN включает в ключ, разбивает один кэшированный объект на множество отдельных копий. Для видеостриминга cache key – самая критичная строка во всём конфиге CDN, потому что одна ошибка может незаметно поместить каждого зрителя в отдельную кэш-ветку, снизить hit ratio с 98% до однозначных значений и за считанные минуты увеличить нагрузку на origin в десять раз. Эта статья объясняет, что входит в стандартный cache key у каждого крупного CDN, разбирает семь типичных ошибок – токен в query string, ловушка Vary, разные hostnames, percent-encoding и регистр, cookies, избыточное географическое ключирование, перекрёстная конфигурация для сегментов и манифестов – и предлагает по одной-двум строкам конфигурации для каждого исправления. Арифметика в конце показывает, как 50 000-зрительский прямой эфир может увеличить исходящий трафик с 180 МБ/с до 6,25 ГБ/с только из-за лишнего токена в ключе; корректный ключ возвращает ситуацию в норму. Лучше прочитать эту статью до следующего запуска стрима, а не после.
Зачем это знать
Cache keys получают недостаточно внимания в литературе по стримингу. В центре внимания – бренд CDN, протоколы HLS, DASH, CMAF и стратегии multi-CDN, а вот cache key, та самая короткая строка, которая определяет, будет ли миллион зрителей использовать один кэшированный сегмент или каждый получит свою отдельную копию, остаётся в конфигурации, которую никто не трогает после первого деплоя. При этом почти каждое инцидентное обращение в продакшене за последние три года, где команда писала «origin горит», заканчивалось одной из трёх причин: cache key включал токен авторизации, query-параметр от плеера или унаследовал значение по умолчанию, разумное для e-commerce, но опасное для live-стрима. Эта статья даёт продакт-менеджеру модель для проверки инженерного плана («мы точно не положили user token в cache key?»), архитектору – названия конкретных «ручек» у каждого вендора, а оператору – семь пунктов чек-листа, которые нужно пройти перед запуском. К концу статьи вы сможете за тридцать секунд прочитать CloudFront Cache Policy, Cloudflare Cache Rule, Fastly VCL или Akamai property и понять, выдержит ли hit ratio стриминга день старта.
Что такое cache key, аккуратно
Cache key – это уникальный идентификатор, используемый для хранения и последующего извлечения данных из кэша. Он позволяет системе быстро определить, какие данные нужно вернуть, не выполняя повторных вычислений или запросов к источнику.
Например, если вы кэшируете результат API-запроса, cache key может быть построен на основе URL, параметров запроса и версии API. При повторном запросе с теми же параметрами система проверяет, существует ли запись с таким ключом в кэше. Если да – данные возвращаются мгновенно. Если нет – выполняется новый запрос, а результат сохраняется под этим же ключом.
Правильный выбор cache key критически важен: слишком общий ключ приведёт к коллизиям и потере актуальности данных, слишком специфичный – к избыточному использованию памяти.
«Важно: cache key должен однозначно описывать контекст запроса, но при этом быть достаточно стабильным, чтобы избежать ненужных промахов в кэше.»
Пример хорошего cache key: api/users/{id}/profile?lang=ru&v=2 → ключ: users:profile:123:ru:v2
Пример плохого cache key: просто users:profile, если не учитывается язык, версия или другие параметры.
Таким образом, cache key – это не просто строка, а продуманная часть архитектуры кэширования, влияющая на производительность и корректность работы системы.
Начнём с определения, которое уточним позже. Cache key – это детерминированный идентификатор, короткая строка, которую кэш формирует из выбранных частей запроса и использует для поиска сохранённого ответа. Когда плеер зрителя запрашивает видеосегмент, CDN не ищет в кэше по URL напрямую: вместо этого он строит cache key на основе URL, выбранных заголовков, cookies и query-параметров, хэширует результат и ищет этот хэш в хранилище. Если ключ совпадает с существующей записью, кэш возвращает сохранённый ответ. Если совпадений нет – кэш запрашивает контент у следующего уровня: shield, регионального кэша или, в крайнем случае, origin; сохраняет полученный ответ под новым ключом и отдаёт его клиенту.
Аналогия для нетехнического читателя – карточка в библиотечном каталоге. На карточке нет самой книги, а только те признаки, по которым библиотекарь её ищет. Библиотека, которая каталогизирует книги только по названию, складывает всех «Гамлетов» в один слот. Библиотека, которая использует для каталогизации название, автора, издание, язык, переплёт и дату последней выдачи, кладёт каждого «Гамлета» в отдельный слот – и библиотекарь не находит ни одной книги на полке, когда десять читателей одновременно спрашивают «Гамлета». Cache keys ведут себя так же. Чем меньше признаков в ключе, тем больше запросов попадают на один и тот же кэшированный объект и тем выше hit ratio. Чем больше признаков – тем чаще каждый запрос получает свою собственную частную копию (в литературе это явление называют cache fragmentation), и тем сильнее падает hit ratio.
В стриминге меняются ставки. Статический сайт может обслуживать сотни уникальных URL из одного cache key: если ключ дробится в десять раз – получается тысяча отдельных копий, и сайт продолжает работать. Live-стрим, напротив, отдаёт одни и те же несколько URL-сегментов всем зрителям. Если ключ дробится даже на небольшой коэффициент – по предпочитаемому языку, по варианту Accept-Encoding или по, казалось бы, безобидному session ID – кэш не вмещает все копии, hit ratio падает, и запросы уходят на origin. Origin перегружается, счёт за CDN резко растёт, плеер начинает буферизоваться в регионах, которые никто не планировал. Cache key – единственная точка во всём стриминг-стеке, где одна строка конфигурации стоит между «всё в порядке» и «всё горит».
Дефолтный ключ кэша у каждого крупного CDN
Дефолты важны: инженер, который ни разу не открывал конфиг, запускает в продакшн то, что ему выдал CDN в первый день. Дефолты у разных вендоров схожи, но различаются в ключевых деталях.
Cloudflare. Дефолт: {scheme}://{host}{path}?{sorted_query_string}. В документации Cloudflare это прямо указано: шаг сортировки внутри правил кэширования по умолчанию упорядочивает параметры строки запроса (query string) в алфавитном порядке, чтобы два URL с одинаковыми параметрами, но в разном порядке, использовали один и тот же кэш-объект. Заголовки и куки в ключе кэширования по умолчанию не участвуют. В разделе Caching на панели управления есть отдельный параметр Query String Sort, который можно включать или отключать, однако внутренняя алфавитная сортировка включена по умолчанию.
AWS CloudFront. Дефолт: URL плюс то, что включает активная Cache Policy. Cache Policy явно перечисляет, какие query strings, заголовки и cookies входят в ключ – это QueryStringsConfig, HeadersConfig, CookiesConfig. CloudFront предоставляет управляемые политики: Managed-CachingOptimized не включает ни одного заголовка, ни одной cookie, ни одного query-параметра в ключ; Managed-CachingDisabled отключает кэширование полностью. Отдельная Origin Request Policy управляет тем, какие заголовки и параметры CloudFront передаёт origin, не включая их в ключ кэша – это разделение особенно важно для стриминга, и мы к нему вернёмся ниже.
Fastly. Дефолт: URL и заголовок Host объединяются в подпрограмме vcl_hash. В документе VCL best practices Fastly прямо предупреждает инженеров о нежелательности изменения этого хэша без необходимости и рекомендует использовать Vary как более гибкий основной подход. Флаг req.hash_always_miss принудительно вызывает промах кэширования без отключения объединения запросов – полезен при отладке, но опасен в продакшене.
Akamai. Дефолт: URL; параметры запроса в ключе кэширования и модификации cache ID настраиваются индивидуально для каждого свойства в Property Manager. В Akamai это называется cache ID – по сути то же самое, что и cache key, но с использованием другой терминологии; поведение Cache ID Modification определяет, какие компоненты ключа добавляются или удаляются.
Google Media CDN. По умолчанию: URL; что дополнительно входит в ключ – задаётся в настройках кэширования.
Сводная таблица:
| CDN | Дефолтный cache key | Чего НЕТ в дефолтном ключе | Vary header учитывается? |
|---|---|---|---|
| Cloudflare | scheme + host + path + сортированный query string | Headers, cookies | Только для allow-list заголовков |
| AWS CloudFront | URL + поля Cache Policy | Всё, что Cache Policy исключает | Учитывается для Accept-Encoding, Accept-Language, Origin |
| Fastly | URL + Host | Headers, cookies | По умолчанию; собирается secondary key по спецификации |
| Akamai | URL (Cache ID) | Headers, cookies, query strings (если не настроено) | По умолчанию игнорируется; есть Remove Vary Header behaviour |
| Google Media CDN | URL | Headers, cookies, query strings (если не настроено) | По конфигурации |
Картина похожая: все вендоры начинают с «URL плюс чуть-чуть» и дают возможность что-то к этому добавлять. Две самые дорогие ошибки в ключе кэша вырастают из того, что инженеры в эту простую основу добавляют слишком много.
Vary header – «половинный близнец» cache key в спецификации
У HTTP-спецификации есть своё мнение о cache keys, и стриминг-инженер, который его проигнорирует, рискует оказаться в неловком споре с поддержкой CDN. Главный документ – IETF RFC 9111, HTTP Caching, июнь 2022, который заменил RFC 7234.
Механизм – это заголовок ответа Vary. Когда сервер возвращает ответ с Vary: Accept-Language, кэш интерпретирует это как соглашение: ответ был выбран на основе заголовка запроса Accept-Language клиента, и кэш не должен отдавать этот сохранённый ответ другому запросу, у которого значение Accept-Language отличается от оригинального. RFC 9111 §4.1 называет получившийся расширенный ключ secondary cache key – первичный ключ остаётся URL + метод, а заголовки запроса, перечисленные в Vary, формируют вторичный ключ для каждого сохранённого ответа. Ответ с Vary: * никогда не совпадает, что заставляет кэш каждый раз выполнять ревалидацию; это прямо указано в спецификации.
Для стриминга последствия могут быть серьёзными и контринтуитивными. Если origin-акселератор ставит Vary: Accept-Encoding, Cookie, User-Agent на каждый манифест и сегмент, каждый зритель с другим User-Agent – будь то Safari, определённая версия Chrome или модель Roku – получает свою отдельную кэш-копию. Один неправильно настроенный заголовок Vary может раздробить кэш в пятьдесят раз по версиям браузеров. Защитная практика на стороне origin: держать Vary коротким, указывать только точные имена заголовков и ни в коем случае не включать User-Agent, Cookie или * в ответах стриминга. Общая рекомендация Apple HLS Authoring Specification по кэшированию говорит то же самое – ключи кэша для сегментов должны быть простыми; сегменты неизменяемы.
Поведение CDN с заголовком Vary различается. Cloudflare учитывает Vary только для небольшого списка разрешённых заголовков (классический пример – Accept-Encoding). Akamai по умолчанию игнорирует большинство заголовков Vary и рекомендует удалять их на edge, если это не Vary: Accept-Encoding. Fastly по умолчанию уважает Vary и формирует вторичный ключ кэша в соответствии со спецификацией. В результате: origin-сервер, полагающийся на стабильную работу Vary, может наблюдать разную hit ratio на разных CDN для одного и того же контента. В качестве защитной практики, которую мы внедряем по умолчанию, рекомендуется явно задавать cache key через правила кэширования CDN, а использовать Vary как страховку, а не основной механизм.
Семь ошибок cache key, которые ломают стриминг
Эти семь ошибок вызывают более девяти из десяти инцидентов с cache key, с которыми мы помогали стриминг-командам разобраться. Приведены в порядке убывания частоты.
Ошибка 1. Session token в query string
Типовой паттерн авторизации в ранних стриминговых стеках – подпись URL путём добавления токена в query: …/segment_4172.ts?token=eyJh…. Токен уникален для каждого зрителя, а иногда и для сессии, а по умолчанию ключ кэширования включает query string. В результате получается одна кэш-копия на зрителя на сегмент; коэффициент попаданий падает почти до нуля.
Исправление – убрать токен из query string и перейти на signed-cookie (Cloudflare Signed Cookies, CloudFront Signed Cookies, Cloud CDN Signed Cookies) или схему подписи URL-префикса, при которой CDN удаляет подписанный сегмент пути из cache key. AWS, Cloudflare, Google, Bunny и Akamai именно такую рекомендацию дают в документации по token-авторизации. Dual-токенная аутентификация в Google Media CDN – канонический пример: подпись мастер-URL краткосрочная и специфичная для зрителя, а подпути под ней подписаны более долгоживущим общим токеном, который исключается из cache key. В результате получается контроль доступа на уровне зрителя при глобально разделяемом кэше.
Ошибка 2. Authorization header в cache key
Вторая разновидность той же проблемы: origin или CDN-политику настроили так, что в ключ кэша включается заголовок запроса Authorization. А заголовок Authorization содержит JWT или непрозрачный токен, который уникален для каждого пользователя. В результате кэш дробится по пользователям, и показатель hit ratio падает. Решение – исключить заголовок Authorization из ключа кэша, но при этом продолжать передавать его на origin для проверки авторизации. Origin Request Policy в CloudFront – как раз тот инструмент, который это обеспечивает: заголовок доходит до origin для валидации, а ключ кэша остаётся чистым.
Ошибка 3. Ловушка Vary
Origin-пакеджер возвращает Vary: User-Agent манифест. CDN учитывает заголовок Vary и шардирует кэш по каждой уникальной строке User-Agent. Обновление версии Mobile Safari, выход новой версии Chrome или появление новой модели Roku – каждое из этих событий порождает отдельный кэшированный объект. В результате снижается hit ratio, а origin начинает сталкиваться с длинным хвостом запросов. Решение – ограничить заголовок Vary на origin небольшим allow-листом (Vary: Accept-Encoding и только его, если нет веской причины) и использовать поведение Akamai Remove Vary Header или аналогичный механизм на Cloudflare/FASTLY в качестве фильтра-страховки на edge.
Ошибка 4. Фрагментация по hostname
Стриминг-нагрузка распределяется между двумя CNAME – cdn1.example.com и cdn2.example.com, которые ведут на один и тот же CDN. Заголовок Host входит в ключ кэширования по умолчанию для каждого CDN. В результате один и тот же сегмент хранится дважды – по одной копии на каждый hostname. Если в нагрузке три hostname, получается три копии, и так далее. Решение – либо направить весь трафик через единый канонический хост, либо использовать нормализацию хоста в CDN (например, опция «Resolved host» в Cloudflare, кэширование только по пути в CloudFront, переписывание req.http.Host в vcl_hash в Fastly), чтобы объединить несколько hostname в один ключ кэширования.
Ошибка 5. Несовпадение percent-encoding и регистра
/segment%204172.ts, /segment 4172.ts и /Segment_4172.ts – это три разных ключа в кэше, даже если они ссылаются на один и тот же файл на origin. Разные версии плееров по-разному кодируют символы: Android-устройство, приводящее путь к верхнему регистру, не может делить кэш с iOS-устройством, использующим нижний регистр. Решение – принудительно применять каноническую форму URL на edge: использовать строчные пути и нормализовать кодировку. Это делается одной правкой через Cloudflare Transform Rules, CloudFront Functions, Akamai Modify Outgoing Request или одной строкой set req.url в vcl_recv у Fastly. Объём работ небольшой, а влияние на hit ratio заметно при каждом мультиплатформенном запуске.
Ошибка 6. Cookies в ключе кэширования
Cookie на сайте отслеживает последний просмотренный эпизод и устанавливается при каждом запросе, включая запросы на сегменты. Политика кэширования CDN включает Cookie в ключ кэширования. В результате каждый зритель получает отдельный кэшированный сегмент, и коэффициент попаданий (hit ratio) падает. Решение – исключить cookies из ключа кэширования для стримингового поддомена. CookiesConfig.CookieBehavior: none в CloudFront – пример такой настройки; Cloudflare по умолчанию не включает cookies в ключ кэширования, поэтому достаточно просто не активировать эту опцию через Cache Rule. Общий принцип: cookies относятся к origin приложения, а не к CDN для стриминга.
Ошибка 7. Перекрёстная конфигурация для манифестов и сегментов
Манифесты изменяемы, сегменты – нет. Разумная конфигурация трактует их по-разному: короткий TTL на .m3u8 и .mpd, длинный TTL на .ts и .m4s. Типичная ошибка – применить один и тот же cache key recipe к обоим. Cache key манифеста наследует long-lived caching от сегмента, апдейты не пропагируются. Или, наоборот, сегменты получают short-TTL recipe, кэш вытесняет их за секунды, и каждый ретрай идёт на origin. Исправление – два отдельных cache rules: один для *.m3u8 / *.mpd / *.dash с TTL 1–5 секунд и включённым serve-stale; второй для сегментов *.ts / *.m4s / *.mp4 с max-age в один год и директивой immutable – плюс проверка перед запуском, что оба правила матчатся на правильные URL-паттерны.
Арифметика: один токен в query string, в десять раз счёт
Возьмём тот же пример из предыдущей статьи раздела. Прямой спортивный трансляция, 50 000 одновременных зрителей при скорости 3 Мбит/с, сегменты HLS длительностью 4 секунды. Система обрабатывает 12 500 запросов на получение сегментов в секунду; каждый сегмент составляет 1,5 МБ (3 Mbps × 4 s ÷ 8 = 1,5 МБ).
Чистый cache key. Ключ – только URL. Edge hit ratio 88%, shield hit ratio 92%, остаточный miss на origin – 0,96%. Арифметика из статьи 6.2:
origin-промахи в сек = 12 500 × 0,12 × 0,08 = 120 fetch/сек
origin egress = 120 × 1,5 МБ = 180 МБ/секCache key с per-viewer session token. Та же нагрузка, но cache key включает ?token=…, и у каждого зрителя токен свой. URL каждого сегмента теперь уникален для каждого зрителя; кэш хранит 50 000 версий сегмента 4172 вместо одной. Edge не вмещает их все, shield не вмещает их все, и почти каждый запрос зрителя на сегмент промахивается мимо всех уровней. Hit ratio на edge падает примерно до 5% – остаточный hit достигается только тогда, когда тот же зритель повторно запрашивает тот же сегмент в пределах cache window. Арифметика:
origin-промахи в сек = 12 500 × 0,95 × 0,95 = 11 281 fetch/сек
origin egress = 11 281 × 1,5 МБ = 16,92 ГБ/секOrigin egress колеблется от 180 МБ/с до 16,92 ГБ/с – это рост в 94× при той же нагрузке. По стандартному тарифу AWS – $0,09 за гигабайт для EC2-выхода в интернет – некорректный стабильный счёт может вырасти с $42 000 в месяц до $3,95 млн в месяц. Даже при 4 часах пикового трафика в день (реалистичный сценарий для спортивного стримера) ошибочный счёт составит около $660 000 в месяц, тогда как корректный – всего $7 000. Всё исправляется одной строкой в правиле кэша.
Смысл арифметики – не в точной цифре, а в порядке величины. Ошибка в cache key не вызывает 20%-ной регрессии. Она приводит к регрессии в 50–100 раз, и настолько быстро, что дежурный инженер успеет заметить рост счётчика на origin раньше, чем сработает алерт мониторинга.
«Типичная ошибка. «Мы уже мониторим cache hit ratio в дашборде». Hit ratio – правильная метрика, но окно обновления дашборда почти всегда длиннее, чем время, за которое деплой с токеном в query string спалит квартальный бюджет CDN. Проводите аудит cache key до деплоя; считайте дашборд страховкой, а не заменой. Пре-лонч-чек-лист ниже – та версия, которую мы используем внутри.»
Пре-лонч-чек-лист по cache key
Короткий, вендорно-агностичный список из семи вопросов, на которые стриминг-команда должна письменно ответить перед любым live-запуском. Эту же версию мы публикуем внизу страницы как компаньон для скачивания.
Семь вопросов:
- Что сейчас в cache key? Выведите точное правило из консоли CDN. Если не удаётся сформулировать его одной фразой – правило слишком сложное.
- Есть ли session token в query string? Если да – перенесите его в signed cookie или в подписанный path-префикс.
- Входит ли заголовок Authorization в cache key? Если да – перенесите его в Origin Request Policy или аналог.
- Что перечисляет заголовок Vary на стороне origin? Если что-то, кроме Accept-Encoding, – обсудите возможность удаления на edge.
- Сколько hostnames раздают один и тот же контент? Если больше одного – нормализуйте к единому хосту в cache key.
- Нормализован ли путь по регистру и percent-encoding? Если нет – добавьте одну строку нормализации на edge.
- Обрабатываются ли .m3u8/.mpd и сегменты отдельными правилами? Если нет – разделите правило.
На каждый вопрос – краткая фраза-ответ и ссылка на строку конфигурации. Ревьюер должен быстро прочитать список, понять логику формирования cache key и одобрить или отклонить его за десять минут.
Где Фора Софт вписывается
Мы строим и эксплуатируем стриминговые стеки с 2005 года – OTT / Internet TV, e-learning, телемедицина, видеонаблюдение, конференц-платформы на WebRTC + HLS-гибридах – и аудит конфигурации cache key перед запуском является обязательной частью передачи каждого проекта. В одном OTT-касе мы сократили месячный счёт оператора за CDN более чем на 80%, убрав session token из cache key. В другом случае запуск e-learning-платформы удалось спасти за три дня до старта когорты, объединив четыре hostnames в один канонический streaming-поддомен. Паттерн повторяется по всем вертикалам: настройка cache key находится на стыке платформенного инженеринга и SRE, и большинство команд недооценивают её важность до первого инцидента.
Шпаргалка по вендорам, 2026
| Задача | Cloudflare | AWS CloudFront | Fastly | Akamai | Google Media CDN |
|---|---|---|---|---|---|
| Дефолтный cache key | scheme + host + path + sorted query string | URL + поля Cache Policy | URL + Host (vcl_hash) | URL (Cache ID) | URL |
| Добавить query в ключ | Cache Rule > Custom Cache Key | Cache Policy > QueryStringsConfig | set req.url в vcl_recv | Cache Key Query Parameters | cdnPolicy.cacheKeyPolicy.includedQueryParameters |
| Передать заголовок без keying | Workers / Transform Rule | Origin Request Policy | req.http.X = …; remove from vcl_hash | Modify Outgoing Request | Origin request configuration |
| Нормализовать host | Resolved host setting | Cache Policy > HeadersConfig (omit Host) | Переписать req.http.Host в vcl_recv | Cache ID Modification > Hostname | Backend bucket host rewrite |
| Срезать Vary на edge | Workers / Response Headers Transform Rule | Response Headers Policy | unset beresp.http.Vary в vcl_fetch | Remove Vary Header | Custom response headers |
Шпаргалка не заменяет вендорную документацию – это индекс, по которому ревьюер за два клика находит нужный экран конфигурации.
Ключевые выводы
- Ключ кэша – это «имя файла» в системе кэширования; содержимое определяет, сколько уникальных копий хранится в кэше.
- По умолчанию у Cloudflare, CloudFront, Fastly, Akamai и Google Media CDN ключ кэша строится по принципу «URL плюс небольшие дополнения»; именно эти добавления сверху нарушают стриминг.
- Семь типичных ошибок – токен в query-параметрах, включение заголовка Authorization в ключ, ловушка Vary, фрагментация по host, несоответствие кодировок, использование cookies, а также перекрёстные правила для сегментов и манифестов – покрывают более 90% инцидентов.
- Одна ошибка с токеном в query-параметрах может увеличить исходящий трафик с origin в 50–100 раз без какого-либо предупреждения.
- Механизм Vary из RFC 9111 – это как бы «половинный близнец» ключа кэша по спецификации; его нужно проектировать одновременно с явной конфигурацией CDN.
- Перед каждым запуском в продакшн проходите чек-лист из семи вопросов; дашборды с hit ratio – это страховка, а не замена тщательной проверки.
Что читать дальше
- Origin shielding и многоуровневое кэширование – дополнительный уровень защиты, перехватывающий запросы, которые не были обслужены на edge-уровне.
- Аутентификация по токенам, подписанные URL и защита origin – способы аутентификации, совместимые с кэшированием.
- Экономика CDN: 95-й перцентиль, commit, overage, transit – как чистый cache key влияет на стоимость в долларах.