Содержание статьи +
- TL;DR
- Зачем это знать
- Что такое cache key, аккуратно
- Дефолтный 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, географическое over-keying, перекрёстная конфигурация для сегментов и манифестов – и даёт по одной-двум строкам конфигурации на каждое исправление. Арифметика в конце показывает, как 50 000-зрительский live-эфир улетает с 180 МБ/с до 6,25 ГБ/с origin egress только из-за лишнего токена в ключе; чистый ключ возвращает картину обратно. Лучше прочитать до следующего live-запуска, а не после.
Зачем это знать
Cache keys получают незаслуженно мало внимания в литературе по стримингу. Заголовки достаются бренду CDN, протоколу – HLS, DASH, CMAF – и multi-CDN-стратегии; а cache key, та самая короткая строчка, которая определяет, делят ли миллион зрителей один кэшированный сегмент или дробят его на миллион частных копий, лежит в конфиге, который никто не открывает после первого деплоя. При этом каждое production-инцидентное обращение по стримингу за последние три года, где команда писала «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 – детерминированный идентификатор, короткая строка, которую кэш собирает из выбранных частей каждого запроса и по которой ищет сохранённый ответ. Когда плеер зрителя запрашивает видео-сегмент, 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 – единственная точка во всём стриминг-стеке, где одна строка конфигурации стоит между «всё в порядке» и «всё горит».
Дефолтный cache key у каждого крупного CDN
Дефолты важны: инженер, который ни разу не открывал конфиг, везёт в продакшн то, что ему дал CDN на первом дне. Дефолты у разных вендоров похожи, но различаются в существенных деталях.
Cloudflare. Дефолт: {scheme}://{host}{path}?{sorted_query_string}. В документации Cloudflare это написано прямо, и шаг сортировки внутри cache rules сортирует query string алфавитно по умолчанию, чтобы два URL с одинаковыми параметрами в разном порядке делили один и тот же объект кэша. Заголовков и cookies в дефолтном ключе нет. В разделе Caching в дашборде есть отдельный пункт Query String Sort, который можно включать или выключать, но внутренний алфавитный sort включён по умолчанию.
AWS CloudFront. Дефолт: URL плюс то, что включает активная Cache Policy. Cache Policy явно перечисляет, какие query strings, headers и cookies входят в ключ – это QueryStringsConfig, HeadersConfig, CookiesConfig. CloudFront поставляет managed-полиси: Managed-CachingOptimized не включает ни одного заголовка, ни одной cookie, ни одного query-параметра в ключ; Managed-CachingDisabled отключает кэш полностью. Отдельная Origin Request Policy управляет тем, что CloudFront пересылает на origin, не делая это частью cache key – это разделение критично для стриминга, и к нему мы вернёмся ниже.
Fastly. Дефолт: URL плюс Host header, всё собирается в subroutine vcl_hash. В документе VCL best practices Fastly прямо предостерегает инженеров от модификации этого хэша без необходимости и рекомендует Vary как более гибкую первичную технику. Флаг req.hash_always_miss принудительно делает miss без отключения request collapsing – полезен в отладке, опасен в продакшне.
Akamai. Дефолт: URL; cache key query parameters и cache ID modifications настраиваются per property в Property Manager. В Akamai эта конструкция называется cache ID – концептуально то же самое, что cache key, но другим словарём; поведение Cache ID Modification – это то, чем добавляют или убирают компоненты ключа.
Google Media CDN. Дефолт: URL; что входит в ключ дополнительно – задаётся в caching configuration.
Сводная таблица:
| 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 плюс чуть-чуть» и дают возможность к этому добавлять. Две самых дорогих ошибки в cache key растут из того, что инженеры в эту простую базу добавляют слишком много.
Vary header – половинный близнец cache key в спецификации
У HTTP-спецификации своё мнение о cache keys, и стриминг-инженер, который его пропускает, окажется в неловком споре с саппортом CDN. Главный документ – IETF RFC 9111, HTTP Caching, июнь 2022, который заменяет RFC 7234.
Механизм – это response-заголовок Vary. Когда origin возвращает ответ с Vary: Accept-Language, кэш читает это как контракт: ответ был выбран на основе request-заголовка Accept-Language клиента, и кэш НЕ ДОЛЖЕН отдавать этот сохранённый ответ другому запросу, у которого Accept-Language не совпадает с оригиналом. RFC 9111 §4.1 называет получившийся расширенный ключ secondary cache key – первичный ключ остаётся URL + method, а перечисленные в Vary request-заголовки формируют per-stored-response вторичный ключ. Ответ с Vary: * никогда не совпадает и заставляет ревалидацию каждый раз; спецификация это явно фиксирует.
Для стриминга последствия большие и контринтуитивные. Если origin-пакеджер ставит Vary: Accept-Encoding, Cookie, User-Agent на каждый манифест и сегмент, каждый зритель с другим User-Agent – каждый Safari, каждая версия Chrome, каждая модель Roku – получает свою частную кэш-копию. Один неудачный Vary header может раздробить кэш в пятьдесят раз по версиям браузеров. Защитная настройка на origin: держать Vary коротким, перечислять только точные имена заголовков и никогда не указывать User-Agent, Cookie или * в стриминг-ответах. Общая рекомендация Apple HLS Authoring Specification по кэшированию говорит то же самое – cache keys для сегментов должны быть простыми; сегменты неизменяемы.
Поведение CDN с Vary разное. Cloudflare уважает Vary только для маленького allow-list заголовков (канонический пример – Accept-Encoding). Akamai по умолчанию игнорирует большинство Vary headers и рекомендует удалять их на edge, если это не Vary: Accept-Encoding. Fastly уважает Vary по умолчанию и строит secondary cache key по спецификации. Следствие: origin, который опирается на стабильное поведение Vary, увидит разные hit ratio на разных CDN для одного и того же контента. Защитная практика, которую мы выкатываем по умолчанию: проектировать cache key явно, через конфигурацию cache rules CDN, а Vary рассматривать как страховку, а не как основной механизм.
Семь ошибок cache key, которые ломают стриминг
Эти семь ошибок дают более девяти из десяти всех инцидентов cache key, с которыми мы помогали стриминг-командам разобраться. Перечислены в порядке частоты.
Ошибка 1. Session token в query string
Типовой паттерн авторизации в ранних стриминг-стеках – подпись URL добавлением токена в query: …/segment_4172.ts?token=eyJh…. Токен уникален на зрителя, иногда на сессию, а дефолтный cache key включает query string. Итог – одна кэш-копия на зрителя на сегмент; hit ratio падает почти в ноль.
Исправление – убрать токен из query string и переключиться на signed-cookie (Cloudflare Signed Cookies, CloudFront Signed Cookies, Cloud CDN Signed Cookies) или схему подписи URL-префикса, где CDN вырезает подписанный сегмент пути из cache key. AWS, Cloudflare, Google, Bunny и Akamai дают именно эту рекомендацию в своей token-auth документации. Dual-token authentication в Google Media CDN – канонический пример: подпись мастер-URL короткоживущая и зрителе-специфическая; sub-paths под ней подписаны более долгоживущим shared токеном, который из cache key вычищается. На выходе – per-viewer access control с глобально разделяемым кэшем.
Ошибка 2. Authorization header в cache key
Вторая разновидность той же проблемы: origin или CDN-полиси включает request-заголовок Authorization в cache key. Authorization несёт JWT или непрозрачный токен, который меняется на каждого зрителя. Кэш дробится по зрителям, hit ratio падает. Исправление – исключить заголовок Authorization из cache key, но всё равно пересылать его на origin для валидации. Origin Request Policy в CloudFront – ровно правильный инструмент: заголовок проезжает на origin для проверки авторизации, cache key остаётся чистым.
Ошибка 3. Ловушка Vary
Origin-пакеджер возвращает Vary: User-Agent на манифест. CDN уважает Vary и шардирует кэш по каждой различной User-Agent строке. Обновление версии Mobile Safari, новая версия Chrome, новая модель Roku – каждое рождает свой кэшированный объект. Hit ratio падает, origin начинает видеть длинный хвост. Исправление – ограничить Vary на origin маленьким allow-list (Vary: Accept-Encoding и больше ничего, если нет конкретной причины) и использовать Akamai Remove Vary Header behaviour или эквивалент на Cloudflare/Fastly как фильтр-страховку на edge.
Ошибка 4. Фрагментация по hostname
Стриминг-нагрузка отдаётся за двумя CNAME – cdn1.example.com и cdn2.example.com, ведущими на тот же CDN. Host header входит в дефолтный ключ у каждого CDN. Кэш держит один и тот же сегмент дважды – по одному на hostname. Если в нагрузке три hostname – три копии, и так далее. Исправление – либо направить весь трафик через один канонический host, либо использовать host-нормализацию CDN (опция «Resolved host» в Cloudflare, cache key только по path в CloudFront, переписывание req.http.Host в vcl_hash у Fastly), чтобы свести несколько hostnames в один ключевой bucket.
Ошибка 5. Несовпадение percent-encoding и регистра
/segment%204172.ts, /segment 4172.ts и /Segment_4172.ts – это три разных ключа на уровне кэша, даже если они указывают на один файл на origin. Плееры в разных версиях по-разному кодируют символы; Android-устройство, которое переводит сегмент пути в верхний регистр, не делит кэш с iOS-устройством, которое держит нижний регистр. Исправление – навязать каноническую форму URL на edge: lowercase пути, нормализация encoding. Делается одной правкой через Cloudflare Transform Rules, CloudFront Functions, Akamai Modify Outgoing Request или одну строку set req.url в vcl_recv у Fastly. Работы немного, влияние на hit ratio измеримо на каждом мультиплатформенном запуске.
Ошибка 6. Cookies в cache key
Cookie на сайте отслеживает последний просмотренный эпизод и устанавливается на каждом запросе, включая запросы за сегментами. Cache policy CDN включает Cookie в ключ. Каждый зритель получает частный кэшированный сегмент, hit ratio умирает. Исправление – держать cookies вне cache key для стриминг-поддомена. CookiesConfig.CookieBehavior: none в CloudFront – пример; Cloudflare cookies в ключ не добавляет по умолчанию, надо просто не включать их через Cache Rule. Общий принцип: cookies – это кэш application-origin, а не streaming-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, в десять раз счёт
Возьмём тот же worked example из предыдущей статьи раздела. Live-спортивный эфир, 50 000 одновременных зрителей при 3 Mbps, 4-секундные HLS-сегменты. Система выдаёт 12 500 fetch-ов сегментов в секунду; каждый сегмент – 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 не вмещает их все, и почти каждый первый запрос зрителя за сегментом промахивается мимо всех слоёв. Edge hit ratio падает примерно до 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-to-public-internet, сломанный steady-state счёт идёт примерно с $42 000 в месяц до $3,95 млн в месяц. Даже на 4 часах пикового трафика в день (реалистичный профиль спортивного стримера) сломанный счёт окажется около $660 000 в месяц, тогда как чистый – $7 000. Одна строка в cache rule.
Смысл арифметики не в абсолютной цифре, а в порядке величины. Ошибка cache key не даёт 20%-регрессии. Она даёт регрессию в 50–100×, и достаточно быстро, чтобы дежурный инженер увидел движение счётчика на счёте origin раньше, чем алерт от мониторинга догонит.
«Типичная ошибка. «Мы уже мониторим cache hit ratio в дашборде». Hit ratio – правильная метрика, но окно обновления дашборда почти всегда длиннее, чем время, за которое деплой с токеном в query string спалит квартальный бюджет CDN. Проводите аудит cache key до деплоя; считайте дашборд страховкой, а не заменой. Пре-лонч-чек-лист ниже – та версия, которую мы используем внутри.»
Пре-лонч-чек-лист по cache key
Короткий, вендорно-агностичный список из семи вопросов, на которые стриминг-команде стоит письменно ответить до любого live-запуска. Эту же версию мы выкатываем как download-компаньон внизу страницы.
Семь вопросов:
- Что сейчас в cache key? Распечатайте точное правило из консоли CDN. Если не можете сформулировать одной фразой – правило слишком сложное.
- Есть ли session token в query string? Если да – перенесите на signed cookie или подписанный path-prefix.
- Входит ли Authorization header в cache key? Если да – перенесите в Origin Request Policy или её эквивалент.
- Что перечисляет Vary header 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 |
Шпаргалка не заменяет вендорную документацию – это индекс, по которому ревьюер за два клика находит нужный экран конфигурации.
Ключевые выводы
- Cache key – это «имя файла» в кэше; то, что в нём, определяет, сколько частных копий хранит кэш.
- Дефолты у Cloudflare, CloudFront, Fastly, Akamai, Google Media CDN – это «URL плюс чуть-чуть»; ломает стриминг то, что в них добавляют сверху.
- Семь ошибок – токен в query, Authorization в ключе, ловушка Vary, фрагментация по host, encoding-несовпадение, cookies, перекрёстные правила для сегментов и манифестов – покрывают более 90% инцидентов.
- Одна ошибка с токеном в query может умножить origin egress в 50–100× без предупреждения.
- Механизм Vary из RFC 9111 – это спецификационный половинный близнец cache key, его надо проектировать вместе с явной конфигурацией CDN.
- Пройдите чек-лист из семи вопросов до каждого live-запуска; дашборды hit ratio – страховка, не замена.
Что читать дальше
- Origin shielding и многоуровневое кэширование – слой, который ловит то, что не поймал edge.
- Аутентификация по токенам, подписанные URL и защита origin – как аутентифицировать, не ломая кэш.
- Экономика CDN: 95-й перцентиль, commit, overage, transit – что в долларах даёт чистый cache key.