Сплит трафика РФ / мир через GeoDNS и CDN
Симптомы
Заголовок раздела «Симптомы»Сайт работает. Из России открывается быстро. Но:
- Telegram не показывает превью ссылки. Кидаете ссылку в чат — вместо карточки с заголовком и картинкой просто синий текст. Иногда превью появляется через раз или только со второй попытки.
- То же самое в других мессенджерах. WhatsApp, Slack, Discord, LinkedIn — карточка либо пустая, либо без картинки.
- Видео и тяжёлые медиа не проигрываются у зарубежных зрителей или стартуют с задержкой в несколько секунд.
- Зарубежные посетители жалуются на «тормозит», хотя в РФ метрики зелёные.
- Google Search Console показывает высокое время ответа при обходе, хотя сервер не нагружен.
Общий признак: всё, что приходит из-за пределов РФ, работает хуже, чем то, что приходит изнутри.
Причина
Заголовок раздела «Причина»Сайт лежит на одном сервере в России. Это нормально для аудитории в РФ и обязательно, если вы работаете с персональными данными по 152-ФЗ. Но каждый зарубежный запрос — это трансатлантический round-trip до Москвы и обратно.
Для человека это «медленно». Для бота — это отказ.
Боты превью ссылок не ждут долго. У краулера Telegram, Slack, Discord жёсткий таймаут на получение HTML: не уложились — карточка не строится, и повторной попытки чаще всего не будет. Складываются три задержки:
- TCP + TLS handshake через полмира — это уже 2–3 RTT до первого байта запроса;
- генерация страницы на origin, если она не в кэше;
- деградация транзита — зарубежные маршруты в РФ нестабильны, потери и всплески latency обычное дело.
При RTT 250–350 мс и холодном кэше вы легко упираетесь в секунду-полторы до первого байта. Бот отваливается. Пользователь видит голую ссылку.
Вторая часть проблемы — медиа. Картинка для og-превью и видео качаются с того же origin. Даже если HTML успел отдаться, картинка может не успеть.
Почему «просто поставить CDN на весь сайт» — плохое решение
Заголовок раздела «Почему «просто поставить CDN на весь сайт» — плохое решение»Очевидный ход: завернуть весь домен в зарубежный CDN. Это чинит зарубежных посетителей и ломает основную аудиторию.
Диапазоны зарубежных CDN у российских операторов периодически деградируют или попадают под фильтрацию. Показательный пример — проблемы Cloudflare в РФ с июня 2025 года: соединения обрываются после первых 16 КБ ответа, то есть сайт «открывается наполовину».
Поставив весь трафик за зарубежный CDN, вы меняете понятную проблему (медленно для 5% аудитории) на непредсказуемую (сайт может лечь для 95%).
Решение: разделить трафик на уровне DNS
Заголовок раздела «Решение: разделить трафик на уровне DNS»Идея простая: пусть DNS отвечает разными адресами в зависимости от того, откуда пришёл запрос.
запрос wpcraft.ru │ ┌────────┴────────┐ │ GeoDNS-зона │ └────────┬────────┘ │ ┌──────────────────┴──────────────────┐ │ │ резолвер в РФ резолвер вне РФ │ │ ▼ ▼ A → origin в РФ Pull Zone → ближайший PoP CDN (прямое соединение, (edge-кэш HTML и статики) CDN в пути нет) │ │ │ промах кэша ▼ ▼ ┌───────────────────────────────────────────────────┐ │ origin.example.ru → тот же сервер │ └───────────────────────────────────────────────────┘Что получаем:
| Кто | Маршрут | Эффект |
|---|---|---|
| Посетитель из РФ | прямое соединение с origin | ничего не изменилось, CDN не в критическом пути |
| Посетитель из-за рубежа | ближайший PoP, отдача из кэша | TTFB падает с ~1 с до десятков мс |
| Бот превью Telegram/Slack | ближайший PoP | укладывается в таймаут, превью строится |
| Поисковый краулер | ближайший PoP | быстрый обход, меньше нагрузка на origin |
Второй origin не нужен, данные никуда не переезжают, 152-ФЗ не затрагивается: сервер один и он в РФ, а edge кэширует только анонимные GET-ответы.
Мы используем для этого Bunny.net — у него есть и DNS с географическими record set, и CDN, и pay-as-you-go тариф без абонентки. Схема не привязана к вендору: любой GeoDNS + pull-CDN подойдёт.
Как это настраивается
Заголовок раздела «Как это настраивается»1. Отдельный хостнейм для origin
Заголовок раздела «1. Отдельный хостнейм для origin»Это первое, что нужно сделать, и самая частая ошибка, если этого не сделать.
Нельзя указывать pull zone на example.ru. Edge-нода при промахе кэша резолвит этот домен через публичный DNS — и, находясь за рубежом, получает «мировую» ветку, то есть саму себя. Петля. Сайт за границей ложится ровно в момент включения гео-сплита.
Решение — отдельная A-запись, которая не участвует в гео-сплите и всегда указывает на реальный сервер:
origin.example.ru. 300 IN A <IP сервера>Pull zone смотрит на https://origin.example.ru, а заголовок Host подменяет на канонический example.ru, чтобы WordPress не начал генерировать ссылки на origin-хостнейм.
На этот хостнейм нужен валидный TLS-сертификат на стороне хостинга — иначе origin придётся опрашивать по HTTP, что для сайта с авторизацией недопустимо.
2. Настройки pull zone, которые ломают WordPress по умолчанию
Заголовок раздела «2. Настройки pull zone, которые ломают WordPress по умолчанию»Bunny (и не он один) даёт дефолты, рассчитанные на раздачу статики. На динамическом WordPress каждый из них по отдельности достаточен, чтобы уронить сайт.
| Параметр | Дефолт | Нужно | Что ломается на дефолте |
|---|---|---|---|
Origin URL | домен сайта | https://origin.example.ru | петля CDN-Loop |
Origin Host Header | пусто | example.ru | WordPress видит не тот домен, ломаются редиректы и канонические ссылки |
Disable cookies | включено | выключить | режет Set-Cookie от origin — не работает вход и гостевая корзина |
Ignore query strings | включено | выключить | ?page=2, UTM и фильтры схлопываются в один ключ кэша — посетитель получает чужую страницу |
Cache-Control override | — | не задавать | заголовки origin должны остаться единственным источником правды |
| TLS 1.0 / 1.1 | включено | выключить | легаси-протоколы |
Отдельно проверьте Verify origin SSL — его стоит включить после того, как на origin-хостнейме появится валидный сертификат.
3. Что не кэшировать
Заголовок раздела «3. Что не кэшировать»Решение о кэшируемости принимаем по методу и пути, а не по кукам:
- метод не
GET/HEAD→ не кэшируем; - путь начинается с
/wp-admin,/wp-login.php,/wp-cron.php,/wp-json,/cart,/checkout, личный кабинет → не кэшируем; - всё остальное — по заголовкам origin.
Почему не по кукам: если сайт уже отдаёт залогиненным пользователям тот же публичный HTML, что и гостям (обычная практика для контентных сайтов с page cache), то bypass по wordpress_logged_in_* отправит каждого авторизованного на PHP и обнулит смысл кэша. Персональные страницы отделяются путём, а не кукой.
Важно: этот список путей обычно дублирует правила кэш-плагина на origin. Это единственная точка рассинхрона в схеме — меняете список на сервере, меняйте и на edge.
4. Заголовки как источник правды
Заголовок раздела «4. Заголовки как источник правды»Edge должен слушать Cache-Control от origin, а не иметь собственный TTL. Тогда время жизни кэша задаётся в одном месте — в коде сайта.
Здесь ждёт неочевидная ловушка. Если на origin стоит кэш-плагин в режиме статических файлов (WP Super Cache в expert-режиме, LiteSpeed, любой вариант с отдачей .html через rewrite), то при попадании в кэш PHP не стартует, файл отдаёт веб-сервер напрямую — и заголовка Cache-Control в ответе нет, только ETag и Last-Modified.
То есть на большинстве ответов edge не получит никакой инструкции. Проверяется в одну команду:
curl -I https://example.ru/ # попадание в статический кэшcurl -I 'https://example.ru/?nocache=1' # промах, отвечает PHPЕсли в первом случае Cache-Control нет, а во втором есть — вы поймали именно это. Чинится на origin: добавить заголовок для отдачи из каталога статического кэша, вне маркеров, которые перезаписывает плагин.
<IfModule mod_headers.c>Header always set Cache-Control "public, max-age=604800" "expr=%{REQUEST_FILENAME} =~ m#/wp-content/cache/supercache/#"</IfModule>Запасной вариант — задать TTL прямо на pull zone. Проще, но переопределяет заголовки глобально: страницы, которые кэшировать нельзя, тоже попадут под общее правило.
5. Сброс кэша при публикации
Заголовок раздела «5. Сброс кэша при публикации»Edge будет держать страницу столько, сколько сказано в Cache-Control. Без принудительного сброса правка текста доедет до зарубежной аудитории через недели.
Минимальный mu-plugin на save_post, edited_term и обновление меню, дёргающий Purge API провайдера, закрывает вопрос. Разумный ориентир — обновление видно за границей в пределах минуты.
6. Реальный IP посетителя
Заголовок раздела «6. Реальный IP посетителя»С мировой ветки origin увидит IP edge-ноды, а не посетителя. Клиентский адрес приезжает в X-Forwarded-For.
Доверять этому заголовку можно только для запросов из диапазонов вашего CDN. Иначе любой желающий подставит произвольный IP в комментарии, логи и антиспам.
7. Геопривязка записей
Заголовок раздела «7. Геопривязка записей»Географические DNS-записи работают по принципу «ближайшая точка», а не по правилу «страна == RU». Поэтому мало завести две ветки — нужны геоякоря:
- ветка РФ — якорь в Москве;
- ветка мира — несколько якорей: Франкфурт, Нью-Йорк, Сингапур, Сан-Паулу.
Без мировых якорей посетители из Финляндии, Польши, Турции окажутся географически «ближе» к Москве и уйдут на origin в обход CDN. Абсолютной точности здесь не будет — это принципиальное ограничение GeoDNS, и его надо принять заранее.
8. Переезд зоны
Заголовок раздела «8. Переезд зоны»Порядок, который экономит нервы:
- Создать зону у нового DNS-провайдера как точную копию текущей — гео-сплит выключен, все ветки отдают origin.
- Сверить записи один в один, запрашивая напрямую у новых NS.
- Сменить NS у регистратора.
- Дождаться распространения.
- И только потом включать гео-сплит.
Два момента, о которых узнают поздно:
TTL делегирования в зоне .RU — 345 600 секунд (4 суток), он задан реестром и не понижается:
dig NS example.ru @a.dns.ripn.netЗначит после смены NS резолверы до четырёх суток продолжат ходить на старого провайдера. Столько же займёт и откат. Старую зону всё это время держим живой и неизменной — это и есть кнопка отката.
CAA-записи могут не переехать. Cloudflare, например, синтезирует их на лету и не хранит в зоне: в экспорте их нет, а после переезда они просто исчезнут. Как минимум нужно завести:
example.ru. 300 IN CAA 0 issue "letsencrypt.org"Без этого CDN не выпустит сертификат для вашего домена, а хостинг — для origin-хостнейма.
9. Сертификат для домена на CDN
Заголовок раздела «9. Сертификат для домена на CDN»CDN обычно валидирует ваш домен по HTTP-01, а для этого домен уже должен указывать на pull zone. Возникает окно: NS переехали, сертификата ещё нет, а поставить домен на CDN без сертификата — TLS-ошибка у всех посетителей.
Чище выпустить сертификат заранее через DNS-01 (валидация TXT-записью, домен при этом никуда указывать не должен) и загрузить его в pull zone вручную. Окна без сертификата не возникает вовсе.
Как проверить, что заработало
Заголовок раздела «Как проверить, что заработало»Резолв из разных точек — из РФ должен приходить origin, извне адреса CDN:
dig example.ru @77.88.8.8 # резолвер в РФ → IP origindig example.ru @8.8.8.8 # резолвер вовне → IP PoP CDNdig origin.example.ru # везде → IP originКэш отрабатывает — второе обращение из-за рубежа должно дать HIT:
curl -sI https://example.ru/ | grep -i 'cdn-cache\|cache-control'Исключения не кэшируются:
for p in /cart /checkout /wp-json/wp/v2/posts /wp-login.php; do printf '%-28s ' "$p" curl -sI "https://example.ru$p" | grep -i 'cdn-cache' || echo 'нет заголовка'doneПревью ссылки. Самая честная проверка исходной проблемы — кинуть ссылку в Telegram и посмотреть, собралась ли карточка. Проверять на ссылке, которую раньше не отправляли: у мессенджеров есть собственный кэш превью, и старая ссылка может отдаться из него.
Функциональные сценарии, которые ломаются чаще всего:
- вход в админку из-за рубежа;
- гостевая корзина переживает переход между страницами;
- пагинация и фильтры отдают разные страницы, а не одну и ту же;
- публикация записи видна за границей в пределах минуты;
- в логах origin виден реальный IP, а не адрес edge-ноды.
Чем за это платим
Заголовок раздела «Чем за это платим»Схема не бесплатная с точки зрения сложности:
- Два слоя кэша с частично дублирующейся конфигурацией — правила на origin и на edge надо держать синхронными вручную.
- Гео-мисроутинг приграничных стран — часть европейской аудитории может уходить на origin в обход CDN.
- Откат не мгновенный — 4 суток TTL делегирования в
.RU. - Диагностика усложняется — на вопрос «почему у клиента не открывается» теперь два разных ответа в зависимости от его геолокации.
Если зарубежная аудитория и превью ссылок для вас не критичны — не начинайте. Если критичны — это дешевле, чем второй origin за рубежом с репликацией данных.
Связанные материалы
Заголовок раздела «Связанные материалы»- CDN в инфраструктуре
- CDN и производительность — сервисы
- Сервисы веб-защиты
- Кэш страниц WordPress
- Инфраструктурный уровень кэша
- Домен и DNS
Материалы и источники
Заголовок раздела «Материалы и источники»- Практика внедрения на wpcraft.ru, 2026
- Bunny.net
- Документация Bunny