Москва: проверенные б/у ноутбуки с гарантией и проверкой перед оплатой
+7 906 722-94-45

Главная → Статьи → CDN для разработчика: подключение и практика

CDN своими руками: как подключить, что кешировать и как инвалидировать

Практика CDN для сайта и API: настройка origin и кеш-заголовков, purge-инвалидация, защита origin, типовые грабли и мониторинг hit ratio.

Прямой ответ

CDN (content delivery network) — сеть edge-серверов, кеширующих контент географически близко к пользователям (RTT до края 10–30 мс вместо 150–200 до единственного дата-центра — для контента это ×2–5 к скорости загрузки в дальних регионах) и снимающих нагрузку с origin; подключение для разработчика — не «купить CDN», а настроить кеш-контракт: (1) origin — ваш сервер (nginx/приложение), который CDN спрашивает при промахе; (2) кеш-заголовки — CDN кеширует по Cache-Control: public, max-age/s-maxage (персональное private/no-store — не кешируется — заголовочная дисциплина — половина настройки CDN уже сделана, если заголовки правильные); (3) DNS-переключение — домен указывает на CDN (CNAME/партнёрская зона), origin-адрес скрывается и защищается (см. ниже); (4) инвалидация — purge по URL/префиксу/тегу при деплое/изменении (API/кнопка — минуты на распространение — версионированные URL с хешем (сборка) уменьшают потребность в purge до нуля для ассетов). Что кешировать: статику с хешами (вечно — immutable), медиа/изображения (долго — изображения), публичный HTML (коротко — s-maxage 30–300 — витринные страницы; личный кабинет — никогда), публичные API-GET (коротко+ETag — справочники/каталог); что не кешировать: авторизованное (Set-Cookie/private — отравление кеша персональными данными — инцидент безопасности №1 в CDN-историях), POST/мутации (мимо кеша по природе), WebSocket/SSE-потоки (проксирование без кеша — passthrough-настройка). Критичная защита — origin shielding: origin должен принимать трафик только от CDN (IP-списки провайдера в файрволе/заголовок-секрет — иначе злоумышленник ходит на origin напрямую — мимо WAF/кеша/лимитов — «CDN включили, DDoS-защиту обошли» — классическая ошибка подключения); и X-Forwarded-For/CF-Connecting-IP — реальный IP клиента теперь в заголовке (логи/rate-limit приложения читают его — nginx real_ip-модуль — прокси-настройка — «все запросы с одного IP» в логах — симптом непрочитанного XFF).

Архитектура: кеш-слои, hit ratio, TTFB

Путь запроса: пользователь → ближайший edge (POP) → кеш-попадание → ответ (без origin — быстро и бесплатно); промах → вышестоящий кеш (shield/региональный — второй шанс без похода к origin) → origin → ответ с кеш-заголовками (edge кеширует по ним и отдаёт); hit ratio — доля попаданий (главная метрика здоровья: контент-сайты 90–98%, SPA-статика 95%+, API-каталоги 70–90% по природе; низкий ratio — причины: TTL короче частоты запросов, Vary-фрагментация (заголовки — User-Agent в Vary убивает ratio), кеш-ключ с игнорируемыми параметрами (URL ?utm_source=x — каждый вариант — свой кеш — нормализация query у провайдера — «игнорировать utm-параметры» — стандартная настройка), «холодный» POP (первый пользователь региона всегда промах — норма); метрики — в дашборде провайдера + свои (мониторинг — hit ratio упал после деплоя — заголовки/Vary изменились — регресс-алерт). TTFB-выигрыш: edge-попадание — 5–30 мс (против 100–250 до origin из дальних регионов) — web vitals глобальной аудитории улучшаются без единой строчки оптимизации кода; origin-разгрузка: пик трафика (распродажа/виральный пост) edge держит кешем — origin видит доли процента запросов — инфраструктура не масштабируется под пики, которые никогда не доходят (экономический аргумент не слабее скоростного); статика при деплое: версионированные ассеты — мгновенная «инвалидация» сменой URL (HTML no-cache ссылается на новые хеши — purge не нужен — деплой атомарен для пользователей).

Настройка: заголовки, инвалидация, защита

Кеш-политика (провайдеры уважают origin-заголовки + свои правила-оверрайды — порядок: origin Cache-Control → правила провайдера (Edge Cache TTL — переопределяет при желании — «не доверять origin» — спорная практика, лучше чинить заголовки): статика — public, max-age=31536000, immutable, HTML-витрины — public, s-maxage=60, stale-while-revalidate=300 (edge держит минуту, браузер не кеширует — деплой виден за минуту без purge; SWR — устаревшее отдаётся мгновенно), API-публичное — public, s-maxage=300 + ETag (304 на edge-проверках — трафик origin минимален); приватное — private, no-store явно (надежда на «CDN сам поймёт по Set-Cookie» — не стратегия). Инвалидация: purge-API (по URL — точечно после правки, по префиксу /images/ — после массовой замены, по тегу/Cache-Tag — «все страницы товара X» — тег-инвалидация элегантнее префиксов — Cloudflare/Similar поддерживают); деплой-пайплайн — purge критичного (HTML-шаблоны) автоматически (CI-хук); частота purge — в лимитах провайдера (тысячи URL — ок, «purge всего на каждый деплой» — кеш обнуляется, origin видит шторм промахов — thundering herd — версионирование решает лучше). Защита origin: файрвол — только IP-диапазоны CDN (публичные списки провайдера — cron-обновление) или секретный заголовок (CDN добавляет X-Origin-Key: secret — origin отклоняет без него — проще IP-списков, слабее при утечке заголовка — комбинировать); скрытие origin-IP (после «засветки» в истории DNS/письмах — смена IP при подключении CDN — иначе прямой доступ тривиален); WAF/rate-limit — на CDN-слое (большинство атак/скрапинга отбивается на edge, не доходя — OWASP-контекст — CDN как первый рубеж); TLS — сертификат на CDN (managed-сертификаты провайдера — автопродление — провайдер берёт на себя) + строгий TLS между CDN и origin. Реальный IP — real_ip-настройка origin (XFF-цепочка — доверять последнему hops от CDN — не всему XFF (подделывается клиентом — spoofing — читать доверенную часть — nginx set_real_ip_from — частая дыра «rate-limit по поддельному IP»).

Практический сценарий: медиа-каталог выходит на CDN за неделю

Исходник: каталог с 40k изображений и витринными страницами — один сервер во Франкфурте; аналитика: 35% трафика — Азия/Америки, LCP у них 4.5–6 с (RTT 180–250 мс + весь контент с origin); сервер в пики (email-рассылки) — CPU 90%, статика съедает канал. Подключение (Cloudflare-класс провайдер): (1) DNS — зона к провайдеру, www/A → CDN (origin — отдельный hostname с новым IP — старый «засвеченный» сменён — прямой доступ закрыт); (2) origin-защита — файрвол на IP-списки провайдера + секретный заголовок (двойная — belt and suspenders); real_ip в nginx (логи/rate-limit (лимиты) на реальных IP); (3) кеш-политика: /media/ (изображения — уже AVIF+хеш в пути — изображения) — max-age год immutable; витринные HTML — s-maxage=120+SWR (деплой → purge-скрипт по тегам витрины — 40 секунд — в CI (пайплайн)); личный кабинет/API-auth — no-store (правило провайдера «bypass cache» на /account/* — явное, не по заголовкам — двойная страховка); /api/catalog — s-maxage=300+ETag; (4) нормализация query — utm-параметры игнорируются кеш-ключом (аналитические метки не фрагментируют кеш — ratio +18 п.п. на витрине); (5) проверка безопасности: учебный «слив персонального» — curl личного кабинета через CDN — MISS+no-store подтверждён; прямой origin без заголовка — 403; XFF-spoof — rate-limit сработал по реальному (real_ip доверяет только CDN-hop); (6) мониторинг: hit ratio/TTFB/bandwidth в Grafana-дашборд (API провайдера → метрики — инструментация) + алерт «ratio < 70% 30 мин» (регресс заголовков ловится). Итоги месяца: hit ratio — 94% (media 99, витрины 88, API 74), origin-трафик −91%, LCP азиатской аудитории — 4.8→1.7 с (edge-RTT + кеш — web vitals — полевые p75), пики рассылок — origin CPU 25% (было 90 — edge держит), инцидент один: деплой сменил схему Cache-Tag без правки purge-скрипта — витрины жили старым 10 минут до ручного purge-all — фикс: теги версионируются вместе с шаблонами (тест purge в CI — тест-дисциплина); стоимость CDN — меньше стоимости одного сервера апгрейда, который не понадобился.

FAQ

Нужен ли CDN маленькому сайту с аудиторией в одном регионе? По скорости — почти нет (аудитория рядом с origin — RTT мал — кеш-выигрыш остаётся, но и nginx-кеш локально закрывает); но CDN-фриту часто бесплатен и даёт: DDoS-фильтрацию, managed-TLS, WAF-базу, скрытие origin — security-довод сильнее скоростного для малых проектов; «CDN обязателен всем» — нет: локальный бизнес с клиентами в одном городе — разумная простота без CDN; глобальный контент/API — да.

Как отлаживать «CDN отдаёт старое»? Чек-лист: URL точно тот же (query/регистр — кеш-ключ точен — ?a=1 и ?a=2 разные), заголовки origin позволяют кешировать дольше, чем прошло (s-maxage=3600 — «час назад закешировано» — штатно), purge дошёл (провайдер-статус/время распространения — минуты), edge-регион свой обновился, а чужой нет (проверка с другого края/инструментами провайдера), промежуточный кеш пользователя/провайдера ISP (редко, но бывает — тест инкогнито/другой сетью); отладка-заголовки: большинство CDN добавляют возраст кеша (CF-Cache-Status: HIT/MISS/EXPIRED, Age: 240) — curl -I показывает историю объекта за секунды; «вечно старое» — обычно origin-заголовки (no-cache забыт на HTML — заголовки), не CDN.

CDN против reverse proxy на своём сервере? Разные этажи, работают вместе: reverse proxy (nginx) — один сервер/кластер (TLS-терминация, кеш локальный, роутинг — nginx); CDN — географическая сеть перед ним (edge-кеши глобально, защита, статика у пользователей); схема «CDN → ваш nginx → приложение» стандартна; «свой nginx = свой CDN» — только для локальной аудитории (географии нет); multi-CDN (несколько провайдеров) — энтерпрайз-уровень (отказоустойчивость провайдера/цены — сложность ×2 — малым не нужно).

Что с кешем при A/B-тестах и персонализации за CDN? Персонализированный HTML не кешируется шаренно (private/no-store — CDN bypass); A/B-раскатка — на origin (A/B-механика — вариант назначается сервером — CDN видит только «пропустить»); персонализация на edge — возможна (edge-функции провайдера по куке/заголовку — вариант из кеш-ключа — продвинутый уровень, нужен при тяжёлом трафике); статика при A/B — общая (варианты меняют разметку, не ассеты — либо версионированные бандлы на вариант); главное правило: кеш-ключ должен включать всё, что меняет ответ (кука варианта — в Vary/ключ — иначе пользователь B получит кеш A — «тест показывает ерунду» — SRM из кеша — классика).

Источники: RFC 9111, документация Cloudflare Cache, Amazon CloudFront Developer Guide — ссылки в шапке.