MAATRIX / Блог / Поставили CDN, а быстрее не стало: четыре случая, когда он бесполезен

Поставили CDN, а быстрее не стало: четыре случая, когда он бесполезен

MAATRIX

Подключили CDN, поменяли NS-записи, подождали сутки на распространение — и открываете сайт с тем же ощущением, что и раньше. WebPageTest показывает почти те же цифры, пользователи продолжают жаловаться на тормоза. Первая реакция — «настроили неправильно», и иногда это действительно так. Но чаще дело не в настройке, а в том, что для конкретного сайта CDN физически не может дать эффект: он решает узкий набор задач, а не ускоряет всё подряд. Разберём четыре ситуации, в которых CDN честно работает так, как спроектирован, — просто это не то ускорение, которого от него ждали.

Случай 1: сайт в основном не про статику

CDN — это сеть кеширующих узлов. Она умеет одну вещь хорошо: отдавать один и тот же ответ множеству разных пользователей с точки, географически близкой к каждому из них. Картинка, файл стилей, JS-бандл, видео — идеальные кандидаты: миллион человек получают один и тот же байт-в-байт файл, и CDN может закешировать его один раз на каждой точке присутствия и раздавать оттуда.

Персонализированная лента, ответ API с данными конкретного пользователя, страница профиля, результат поиска по фильтрам, содержимое корзины — это другое. Такой ответ либо уникален для каждого запроса, либо часто меняется, и кешировать его либо нельзя (испортите пользователю чужие данные), либо бессмысленно (следующий запрос всё равно другой).

Если разложить время загрузки страницы на составляющие, часто оказывается, что статика (картинки, шрифты, CSS, JS) — это уже не главная статья расходов. Браузер кеширует её локально после первого визита, а передача файлов по HTTP/2 или HTTP/3 достаточно быстрая даже без CDN на не самом дальнем расстоянии. Основное время уходит на рендер и гидратацию SPA на клиенте, на последовательные запросы к API, каждый из которых ждёт ответа backend'а и базы, и на TTFB динамической HTML-страницы, которую сгенерировал ваш сервер, а не отдал кеш.

Если, условно говоря, девять десятых времени открытия страницы приходится на запросы, которые CDN физически не может закешировать (потому что это персонализированный ответ или POST-запрос), то оставшаяся десятая часть, которую CDN ускорит, погоды не сделает. Это и есть суть мифа, который разбирали отдельно: «Миф: CDN ускорит любой сайт» — CDN ускоряет то, что кешируется, а не всё, что грузится.

Практический вывод: прежде чем подключать CDN, посмотрите в DevTools на вкладку Network при холодной загрузке страницы (без кеша браузера) и честно оцените, сколько миллисекунд уходит на статику, а сколько — на динамические запросы. Если статика — это доли от общего времени, CDN даст такую же долю ускорения, не больше. Для сайтов, где основная нагрузка — это API и персонализация, куда больше пользы принесёт оптимизация самого backend'а, кеш на уровне приложения (Redis, Memcached) для повторяющихся вычислений и работа над TTFB, чем сеть доставки статики.

Случай 2: аудитория и так рядом с origin-сервером

Идея CDN — сократить физическое расстояние между пользователем и точкой, которая отдаёт ответ. Она работает, только если у CDN есть точка присутствия (PoP), которая реально ближе к пользователю, чем ваш origin-сервер. Если у вас вся аудитория в одном городе или регионе, а сервер уже стоит в дата-центре в этом же регионе, CDN просто нечего сокращать — расстояние и так минимальное.

Хуже того: в некоторых случаях лишний слой между пользователем и origin добавляет накладные расходы, а не убирает их. Запрос сначала идёт до ближайшего PoP CDN, и если там кеш-промах (см. случай 3), PoP должен связаться с origin — а маршрут от PoP до origin не обязательно короче и быстрее прямого маршрута от пользователя до origin. Расстояние на карте вообще плохо коррелирует с реальной сетевой задержкой: она зависит от маршрутизации между автономными системами, пиринга и договорённостей между провайдерами, а не от километров по прямой — это подробно разбирали в статье «Почему пакет из Москвы в Питер идёт через Стокгольм». Та же логика работает и в обратную сторону: близкий по карте PoP CDN не гарантированно ближе по факту, если маршрут до него петляет.

Признаки этой ситуации: логи или аналитика показывают, что подавляющая часть аудитории из одного региона; этот регион совпадает (или почти совпадает) с локацией вашего origin-сервера; замер задержки напрямую до origin из этого региона уже показывает низкие цифры.

Если это ваш случай, CDN даст эффект только за счёт снятия нагрузки с origin при пиковом трафике (второй эффект CDN, не связанный с географией) — но не за счёт сокращения задержки для конечного пользователя. Это стоит проверить отдельно: сравните round-trip time до вашего origin и до ближайшего PoP CDN для реальных IP-адресов вашей аудитории (не из своего офиса — оттуда картина может быть совсем другой). Если разница на уровне погрешности измерения, ускорения не будет, и решать нужно другую задачу — например, снятие нагрузки, а не задержку.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Случай 3: заголовки кеширования настроены не так, как вы думаете

Это самая частая техническая причина, по которой CDN «стоит, но не работает». CDN кеширует ответ только если origin явно разрешил это через HTTP-заголовки — прежде всего Cache-Control (и в некоторых конфигурациях Expires, Vary, ETag). Если заголовки не выставлены вообще, большинство CDN по умолчанию либо не кеширует ничего, либо кеширует очень консервативно. Если заголовки выставлены неправильно — например, Cache-Control: no-store стоит на статике по ошибке, или Vary: Cookie присутствует там, где куки не влияют на содержимое, — CDN формально видит команду «не кешировать» или «кешировать отдельно под каждое значение куки» и на практике промахивается почти на каждый запрос.

Результат — тот самый парадокс: CDN подключен, трафик через него идёт, а хит-рейт стремится к нулю, потому что каждый запрос — это MISS, который CDN всё равно пересылает на origin, добавляя к прямому пути ещё один прыжок (round-trip до PoP, потом round-trip от PoP до origin). Получается не ускорение, а замедление: пользователь платит задержкой за лишний хоп, а origin всё равно обрабатывает запрос как обычно.

Типичные ошибки в заголовках:

# Заголовок вообще не выставлен — CDN решает по своим умолчаниям
HTTP/1.1 200 OK
Content-Type: image/png

# no-cache здесь работает не так, как кажется по названию:
# это не "не кешировать", а "кешировать, но перепроверять у origin каждый раз"
Cache-Control: no-cache

# Явный запрет любого кеширования на статике по ошибке (скопировали с API-эндпоинта)
Cache-Control: no-store, private

# Vary: Cookie на статике, которая от кук не зависит вообще —
# CDN будет держать отдельную копию под каждое уникальное значение куки
Vary: Cookie

Как должно выглядеть для типичной статики, которая не меняется без деплоя (например, файлы с хешем в имени, app.a1b2c3.js):

Cache-Control: public, max-age=31536000, immutable

Для HTML-страниц, которые меняются, но не персонализированы (например, публичная статья блога):

Cache-Control: public, max-age=300, stale-while-revalidate=60

Проверить реальные заголовки ответа можно без панели CDN, просто curl'ом:

curl -I https://ваш-домен/путь-к-статике.js

Смотрите на Cache-Control в ответе origin (без CDN) и на заголовок статуса кеша самого CDN — у большинства провайдеров он называется X-Cache, CF-Cache-Status, X-Cache-Status или похоже, значение HIT/MISS/EXPIRED/DYNAMIC (имя заголовка и набор значений отличается от провайдера к провайдеру — проверяйте документацию своего). Если на статике, которая точно не меняется, вы стабильно видите MISS, проблема почти наверняка в заголовках или в правилах кеширования на стороне CDN, которые их игнорируют.

Случай 4: собственная латентность CDN — TLS и DNS до провайдера

CDN — это не бесплатный слой. Прежде чем браузер получит первый байт от точки присутствия CDN, ему нужно: резолвнуть DNS-имя (а у большинства CDN оно отдельное от вашего origin, часто через CNAME на инфраструктуру провайдера), установить TCP-соединение и провести TLS-рукопожатие с этим конкретным узлом. Если у пользователя холодный DNS-кеш (первый визит на сайт, или TTL записи истёк, или это мобильная сеть с агрессивной очисткой кеша резолвера), вся эта цепочка добавляется к времени ответа — и для части запросов может съесть весь выигрыш, который дало бы физическое приближение к пользователю.

Из чего складывается эта накладная задержка: DNS-резолвинг до имени CDN (даже с коротким TTL каждый холодный резолв — это дополнительные миллисекунды, а иногда и рекурсивный поход резолвера через несколько уровней; механику этой цепочки разбирали в статье «DNS отвечает 200 мс — это треть времени открытия сайта»); TCP handshake до узла CDN — три пакета туда-обратно, если это не 0-RTT-сценарий; TLS handshake — ещё один-два round-trip'а в зависимости от версии TLS и наличия session resumption у клиента с этим конкретным узлом (а у нового посетителя его, по определению, нет).

Для повторного визита (тёплый DNS-кеш браузера, живое TLS-соединение или session ticket) эти издержки почти исчезают, и CDN действительно выигрывает у прямого соединения с origin. Но для первого визита, для нестабильного мобильного соединения или сценариев, где каждый запрос — новое соединение (например, API-клиент без keep-alive), накладные расходы на установку соединения с CDN могут быть сопоставимы с тем расстоянием, которое CDN должен был сократить. Сравнение «CDN против прямого соединения» — это не всегда «дальше против ближе», а иногда «известный, прогретый маршрут против нового, с холодным стартом».

Проверить вклад этой части можно через тот же DevTools Network — там отдельно видны фазы DNS Lookup, Initial Connection, SSL для каждого запроса. Если для запросов к CDN эти фазы занимают заметную долю от общего времени ответа, а для того же самого домена при повторной загрузке страницы (тёплые кеши) — почти ничего, вы наблюдаете именно эту накладную задержку, а не проблему самого CDN.

Как проверить, что CDN действительно помогает

Вместо того чтобы гадать, в каком из четырёх случаев вы оказались, полезнее сразу измерить три вещи.

1. Заголовок статуса кеша на реальных запросах. Пройдитесь curl'ом или через DevTools по ключевым URL — главной странице, паре статичных файлов, паре API-эндпоинтов — и посмотрите на заголовок X-Cache (или аналог конкретного провайдера). Если на статике не HIT, ищите проблему в заголовках Cache-Control (случай 3). Если на динамике MISS/DYNAMIC — это нормально, так и должно быть.

2. Hit ratio по логам или панели CDN. Почти все провайдеры показывают процент попаданий в кеш за период. Если он низкий (условно, меньше половины) для трафика, который в принципе кешируемый, — снова смотрите на заголовки и правила кеширования. Если он низкий, потому что большая часть трафика — это динамика, которая физически не кешируется, — это случай 1, и низкий hit ratio здесь не баг, а честное отражение природы сайта.

3. Latency до и после для целевой аудитории — именно её, а не вашего собственного соединения. Замер из офиса или с домашнего компьютера разработчика показывает задержку до одной точки, которая может не иметь отношения к тому, где реально сидят пользователи. Нужны замеры из тех регионов, где реально находится аудитория — синтетические инструменты с распределёнными зондами (globalping и подобные) или, если есть возможность, RUM-метрики (Real User Monitoring) из браузеров реальных посетителей. Подробный план таких замеров есть в статье «Выбираем локацию сервера по замерам своей аудитории» — тот же подход применим и к вопросу «помогает ли CDN», а не только к выбору дата-центра.

Сравнивайте не средние значения, а перцентили — p50 и особенно p95/p99. CDN часто выигрывает не в среднем, а именно в хвосте распределения: подтягивает тех пользователей, у кого раньше был самый плохой маршрут до origin. Если смотреть только на среднее, этот эффект легко потерять на фоне шума.

Если CDN не дал эффекта: что делать дальше

Если после диагностики вы попали в один из четырёх случаев, действия разные:

  • Случай 1 (динамика). CDN оставляйте только под статику и, возможно, edge-кеширование отдельных кешируемых GET-ответов API (с явным Cache-Control и коротким TTL). Основные усилия — в оптимизацию backend'а и кеш на уровне приложения.
  • Случай 2 (близкая аудитория). Пользу ищите не в задержке, а в снижении нагрузки на origin при пиках и в защите от части паразитного трафика на уровне edge. Если и это не нужно, честно признайте, что CDN здесь лишний слой, и уберите его.
  • Случай 3 (кривые заголовки). Перепроверьте Cache-Control на каждом типе контента: статика с хешем в имени — максимальный TTL и immutable; HTML — короткий TTL плюс stale-while-revalidate; всё уникальное для пользователя — явный no-store или private. Отдельно проверьте Vary — он должен перечислять только заголовки, которые реально меняют содержимое ответа.
  • Случай 4 (латентность до CDN). Здесь мало рычагов на вашей стороне — в основном выбор провайдера с плотной сетью PoP рядом с аудиторией и разумный TTL DNS-записи. Для API-клиентов без постоянного соединения эффект от CDN будет скромнее по определению — там больше пользы даёт keep-alive и мультиплексирование HTTP/2/3.

И общий совет: не ставьте CDN «на всякий случай». Это инструмент под конкретную механику — приближение кешируемого контента к пользователю и снятие нагрузки с origin. Если ни одно из двух не актуально для вашего трафика, CDN — это просто ещё один компонент инфраструктуры, за настройкой и мониторингом которого нужно следить, без соразмерной пользы взамен.

Нужен сервер под эту задачу?

Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Арендовать сервер

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.

Частые вопросы

CDN совсем бесполезен для динамического сайта?

Не совсем — он снимает нагрузку с origin на статике и может кешировать отдельные GET-ответы API при явной настройке заголовков. Но задержку для основной динамической части он не уберёт — это физическое ограничение механики кеша, а не вопрос настройки.

Как быстро понять, попадает ли запрос в кеш CDN?

Посмотрите заголовок ответа вроде X-Cache, CF-Cache-Status или аналог вашего провайдера — через curl -I или вкладку Network в DevTools.

Если hit ratio низкий, это всегда проблема настройки?

Нет. Низкий hit ratio на сайте с преимущественно персонализированным трафиком — ожидаемое поведение, а не баг. Проблема, если он низкий именно на статике или явно кешируемом контенте.

Может ли CDN сделать сайт медленнее?

Да: когда каждый запрос — промах кеша и CDN добавляет лишний прыжок до origin вместо прямого пути; и когда накладные расходы на TLS/DNS до самого CDN для холодного посетителя перевешивают выигрыш от близости.

Стоит ли мерить задержку из своего офиса?

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

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →