CDN-кеширование и TTL-оптимизация
Краткое резюме
CDN-кеш — это «ускоритель + щит» между пользователем и origin. Он работает хорошо, когда:1. Ключ кэша (cache key) стабилен и не содержит «шума».
2. TTL-политика под нагрузку: `s-maxage`/`max-age` + `stale-while-revalidate/if-error`.
3. Инвалидация управляется: по тегам/префиксам + «мягкие» purge.
4. Включены tiered-cache/origin-shield и negative-cache.
5. Есть наблюдаемость: hit-ratio по слоям, p95 TTFB, доля возвратов 304.
Базовые хедеры и что они значат
`Cache-Control`:- `max-age=
` — TTL для браузера. - `s-maxage=
` — TTL для CDN/прокси (перекрывает `max-age`). - `stale-while-revalidate=
` — отдаем устаревшее, параллельно обновляем. - `stale-if-error=
` — отдаем устаревшее при ошибке origin. - `immutable` — ресурс не меняется (подходит для версиированных ассетов).
- `ETag` / `Last-Modified` — условия для 304, экономят байты/CPU origin.
- `Vary` — список заголовков, влияющих на ключ кэша (использовать сдержанно!).
- `Surrogate-Control` — «расширенный» Cache-Control для CDN (если поддерживается).
- `Expires` — устаревший, но все еще учитывается клиентами.
Cache-Control: public, max-age=31536000, immutable
Пример (полу-динамика с безопасным устареванием):
Cache-Control: public, s-maxage=300, max-age=60, stale-while-revalidate=600, stale-if-error=86400
ETag: "a1c3..."
Ключ кэша: дизайн и нормализация
Цель — чтобы одинаковые по сути запросы попадали в один и тот же объект.
Нормализация URL: регистр, двойные слэши, трейлинг-слэш, порядок query-параметров.
Игнор «шума»: `utm_`, `fbclid`, `gclid`, произвольные реф-теги.
Ограниченный Vary: только реально значимые заголовки (`Accept-Encoding`, иногда `Accept`, `Accept-Language` для локали).
Device-class: если нужно, используйте 2–3 класса (mobile/desktop/tablet), а не бесконечные user-agent ветки.
Auth-контекст: по умолчанию не кешируйте приватное; используйте signed-URLs/cookies-bypass или разделение публичных/приватных путей.
Surrogate-Key: product:123 catalog
Cache-Control: public, s-maxage=300, stale-while-revalidate=600
Vary: Accept-Encoding
TTL-стратегии по типам контента
Политики инвалидации
By URL/Prefix: «смести все под `/static/2025-11-05/`».
By Tag/Key: «снять весь `catalog` и `product:123`».
Soft purge: пометить как устаревшее, не стирать объект — быстрее повторное наполнение.
Event-driven: CI/CD или админ-событие вызывает webhook «invalidate tags».
Рекомендация: объединяйте обе тактики: версионирование путей для ассетов + tag-purge для контента/страниц.
Tiered-cache, origin-shield и prewarm
Tiered-cache: региональные слои CDN → меньше запросов к origin.
Origin-shield: один «щитовой» POP к origin — улучшает локальность и hit-ratio.
Prewarm (pre-fetch): прогрев горячих URL/кэшей перед ивентом/релизом.
Negative-cache: кратковременно кешируйте 5xx/Timeout (30–120 с), чтобы не завалить origin штормом ретраев.
Кеш API: когда можно
Только GET/HEAD и идемпотентные.
Ключ: путь + существенные query (например, `?category=...&page=...`).
Валидация: `ETag`/`Last-Modified` и короткий `s-maxage`.
Фильтры по пользователю: вынесите персонализацию на клиент/edge-функцию или используйте signed-requests + «публичный» ответ.
Cache-Control: public, s-maxage=30, max-age=5, stale-while-revalidate=120, stale-if-error=600
ETag: "feed-v42"
Защита от отравления кэша (cache poisoning)
Жесткая нормализация URL/заголовков; белый список параметров в ключе.
Обрезка подозрительных заголовков/дубликатов (`X-Forwarded-`, расширенные `Accept`).
Ограничение `Vary` и контроль размера/кол-ва заголовков.
Разделение доменов: приватное/админ — на отдельном имени без кэша.
Валидация ответов: не кэшируйте 4xx (кроме 404 для статик), не кэшируйте «пользовательские» страницы без явной политики.
Сжатие и форматы
Brotli для текстовых (js/css/json), gzip — fallback; пред-сжатые ассеты допустимы.
Images: webp/avif там, где поддержка; используйте `Vary: Accept` + производные деривативы.
Range-requests для видео/аудио: CDN кэширует чанки.
Content-Negotiation: держите низкий кардиналитет ключа (device-class вместо сырых UA).
Наблюдаемость и SLO
Ключевые метрики
Hit-ratio (by bytes/requests) на edge/tier/shield.
p50/95/99 TTFB по регионам и типам (статик/API).
Fill-rate/Origin egress — сколько уходит к origin.
304 rate и средний размер ответа.
Error budget: доля `stale-if-error`/`SWR` выдач; частота purge.
Примеры SLO
`p95 TTFB` статики регионально ≤ 120–150 мс, API GET кешируемых ≤ 200–250 мс.
Edge hit-ratio статики ≥ 90%, полу-динамики ≥ 60%.
Доля ответов из stale-ветки при ошибках ≤ 0.5% за 30 дней.
Конфиг-шпаргалки
Nginx (reverse-proxy перед CDN или в self-PoP)
nginx proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=CDN:512m max_size=100g inactive=7d;
map $args $clean_args {
"~(^ &)(utm_ gclid fbclid) """; # default $ args simplified example;
}
server {
listen 443 ssl http2;
set $cache_key "$scheme$request_method$host$uri?$clean_args $http_accept $http_accept_encoding";
location /static/ {
proxy_cache CDN;
proxy_cache_key $cache_key;
proxy_ignore_headers Set-Cookie;
add_header Cache-Control "public, s-maxage=86400, max-age=3600, stale-while-revalidate=600" always;
proxy_pass https://origin_static;
}
location /api/public/ {
proxy_cache CDN;
proxy_cache_key $cache_key;
proxy_cache_valid 200 30s;
add_header Cache-Control "public, s-maxage=30, max-age=5, stale-while-revalidate=120, stale-if-error=600" always;
proxy_set_header If-None-Match $upstream_http_etag;
proxy_pass https://origin_api;
}
}
Envoy (SWR + negative-cache, концепт)
yaml http_filters:
- name: envoy. filters. http. cache typed_config:
"@type": type. googleapis. com/envoy. extensions. filters. http. cache. v3. CacheConfig typed_config:
"@type": type. googleapis. com/envoy. extensions. cache. simple_http_cache. v3. SimpleHttpCacheConfig
Cache-Control/Surrogate-Control Header Cache Policies
We cache 5xx errors briefly via route/retry policy + local_rate_limit
Хедеры для «быстрых» ассетов
Cache-Control: public, max-age=31536000, immutable
ETag: "hash"
Content-Encoding: br
Хедеры для полу-динамики (каталоги)
Cache-Control: public, s-maxage=600, max-age=120, stale-while-revalidate=1800, stale-if-error=86400
Vary: Accept-Encoding, Accept
FinOps: как кэш экономит деньги
Egress origin ↓, меньше CPU/DB-нагрузки → ниже инфраструктурные расходы.
Меньше запросов до платных бэкендов (search/index/images).
Целевая метрика: $/снижение p95 и $/снижение egress на 1 ГБ — отслеживайте послезапусковой эффект.
Специфика для iGaming/финтех
Каталоги провайдеров/ассеты: версионированные пути + годичный TTL.
Лендинги событий/турниров: 1–5 мин `s-maxage` + `SWR` на 10–30 мин; tag-purge при обновлении.
Лив-страницы (коэффициенты/таблицы): частичный кэш JSON-блоков, короткие TTL (5–30 с), для персональных блоков — клиентский рендер.
PSP/платежные эндпоинты: не кешируем, строгое `no-store`; кэшируйте только справочники (BIN-таблицы, статусы).
Антибот: кэширование статик/GET, «серые» маршруты для подозрительных ASN; не допускайте `Vary` по шумным заголовкам.
Чек-лист внедрения
- Описан ключ кэша: нормализация URL, список допускаемых query, `Vary` только по нужному.
- Разделены публичные/приватные пути; приватное — `no-store` и bypass CDN.
- Введены TTL-лестницы по типам контента; настроен `SWR/if-error`.
- Настроены tiered-cache + origin-shield; включен negative-cache 5xx (короткий).
- Есть tag/URL purge, soft purge; интеграция с CI/CD.
- Включена компрессия (br/gzip), веб-форматы изображений и range-ответы.
- Метрики: hit-ratio by layer, p95 TTFB, 304 rate, origin egress; алерты на провалы.
- Плейбуки: прогрев кэша перед пиками, экстренный purge, деградация origin.
Типичные ошибки
Без-версио ассеты с большим TTL → «залипшие» бандлы у пользователей.
Чрезмерный `Vary` (по `User-Agent`, всем заголовкам) → взрыв кардинальности и низкий hit-ratio.
Кэширование 4xx/401/403/приватного контента.
Отсутствие negative-cache → лавина запросов на деградный origin.
Нет tag-purge → массовые точечные purge и «шторм» re-fill.
Ключ кэша включает «шумные» UTM/реф-параметры.
Слишком короткий TTL для статики → лишняя нагрузка на CDN и origin.
Мини-плейбуки
1) Прогрев кэша перед событием
1. Сбор топ-N URL по логам → 2) Параллельный prefetch (rate-limited) по регионам → 3) Проверка hit-ratio ↑ и p95 ↓.
2) Экстренный soft-purge катологов
1. Отправить `PURGE`/tag-clear → 2) CDN отдает stale и фоном подтягивает свежие → 3) Проверить отсутствие шипов на origin.
3) Отказ origin
1. `stale-if-error` выручает X часов → 2) Включить баннер «техработы» на edge → 3) По восстановлении — целевой warm-up.
Итог
Сильная CDN-стратегия = правильный ключ кэша + осмысленные TTL с SWR/if-error + управляемая инвалидация + tiered/shield + наблюдаемость. Зафиксируйте политику в хедерах и IaC, измеряйте hit-ratio и p95, планируйте прогрев под пики — и пользователи всегда получат быстрый ответ, а origin останется жив даже в самый горячий час.