﻿---
title: Сплит трафика РФ / мир через GeoDNS и CDN
description: Telegram не показывает превью ссылки, зарубежные посетители ждут ответа секунду — потому что сайт живёт на одном сервере в РФ. Разбор решения через географическое разделение DNS и edge-кэш Bunny.
---

## Симптомы

Сайт работает. Из России открывается быстро. Но:

- **Telegram не показывает превью ссылки.** Кидаете ссылку в чат — вместо карточки с заголовком и картинкой просто синий текст. Иногда превью появляется через раз или только со второй попытки.
- **То же самое в других мессенджерах.** WhatsApp, Slack, Discord, LinkedIn — карточка либо пустая, либо без картинки.
- **Видео и тяжёлые медиа не проигрываются** у зарубежных зрителей или стартуют с задержкой в несколько секунд.
- **Зарубежные посетители жалуются на «тормозит»**, хотя в РФ метрики зелёные.
- **Google Search Console показывает высокое время ответа** при обходе, хотя сервер не нагружен.

Общий признак: всё, что приходит **из-за пределов РФ**, работает хуже, чем то, что приходит изнутри.

## Причина

Сайт лежит на одном сервере в России. Это нормально для аудитории в РФ и обязательно, если вы работаете с персональными данными по 152-ФЗ. Но каждый зарубежный запрос — это трансатлантический round-trip до Москвы и обратно.

Для человека это «медленно». Для бота — это **отказ**.

Боты превью ссылок не ждут долго. У краулера Telegram, Slack, Discord жёсткий таймаут на получение HTML: не уложились — карточка не строится, и повторной попытки чаще всего не будет. Складываются три задержки:

1. TCP + TLS handshake через полмира — это уже 2–3 RTT до первого байта запроса;
2. генерация страницы на origin, если она не в кэше;
3. деградация транзита — зарубежные маршруты в РФ нестабильны, потери и всплески latency обычное дело.

При RTT 250–350 мс и холодном кэше вы легко упираетесь в секунду-полторы до первого байта. Бот отваливается. Пользователь видит голую ссылку.

Вторая часть проблемы — медиа. Картинка для og-превью и видео качаются с того же origin. Даже если HTML успел отдаться, картинка может не успеть.

## Почему «просто поставить CDN на весь сайт» — плохое решение

Очевидный ход: завернуть весь домен в зарубежный CDN. Это чинит зарубежных посетителей и ломает основную аудиторию.

Диапазоны зарубежных CDN у российских операторов периодически деградируют или попадают под фильтрацию. Показательный пример — [проблемы Cloudflare в РФ с июня 2025 года](https://wpcraft.ru/kb/practices/web/security/web-protection-services.md): соединения обрываются после первых 16 КБ ответа, то есть сайт «открывается наполовину».

Поставив весь трафик за зарубежный CDN, вы меняете понятную проблему (медленно для 5% аудитории) на непредсказуемую (сайт может лечь для 95%).

## Решение: разделить трафик на уровне DNS

Идея простая: пусть **DNS отвечает разными адресами в зависимости от того, откуда пришёл запрос**.

```mermaid
flowchart TD
    accTitle: Сплит трафика РФ / мир через GeoDNS и CDN
    accDescr: DNS отдаёт российскому резолверу адрес origin, зарубежному — ближайший PoP CDN; промах кэша на edge уходит на тот же origin.

    R["запрос wpcraft.ru"] --> G["GeoDNS-зона"]
    G --> WW["резолвер вне РФ"]
    G --> RU["резолвер в РФ"]
    WW --> P["Pull Zone → ближайший PoP<br/>кэш HTML и статики"]
    RU --> A["A → origin в РФ<br/>CDN не в пути"]
    P -- промах кэша --> O["origin.example.ru — тот же сервер"]
    A --> O
```

Что получаем:

| Кто | Маршрут | Эффект |
|---|---|---|
| Посетитель из РФ | прямое соединение с 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

Это первое, что нужно сделать, и самая частая ошибка, если этого не сделать.

Нельзя указывать 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 по умолчанию

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. Что не кэшировать

Решение о кэшируемости принимаем по **методу и пути**, а не по кукам:

- метод не `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. Заголовки как источник правды

Edge должен слушать `Cache-Control` от origin, а не иметь собственный TTL. Тогда время жизни кэша задаётся в одном месте — в коде сайта.

Здесь ждёт неочевидная ловушка. Если на origin стоит кэш-плагин в режиме статических файлов (WP Super Cache в expert-режиме, LiteSpeed, любой вариант с отдачей `.html` через rewrite), то при попадании в кэш **PHP не стартует**, файл отдаёт веб-сервер напрямую — и заголовка `Cache-Control` в ответе нет, только `ETag` и `Last-Modified`.

То есть на большинстве ответов edge не получит никакой инструкции. Проверяется в одну команду:

```sh
curl -I https://example.ru/            # попадание в статический кэш
curl -I 'https://example.ru/?nocache=1'  # промах, отвечает PHP
```

Если в первом случае `Cache-Control` нет, а во втором есть — вы поймали именно это. Чинится на origin: добавить заголовок для отдачи из каталога статического кэша, вне маркеров, которые перезаписывает плагин.

```apache
<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. Сброс кэша при публикации

Edge будет держать страницу столько, сколько сказано в `Cache-Control`. Без принудительного сброса правка текста доедет до зарубежной аудитории через недели.

Минимальный mu-plugin на `save_post`, `edited_term` и обновление меню, дёргающий Purge API провайдера, закрывает вопрос. Разумный ориентир — обновление видно за границей в пределах минуты.

### 6. Реальный IP посетителя

С мировой ветки origin увидит IP edge-ноды, а не посетителя. Клиентский адрес приезжает в `X-Forwarded-For`.

Доверять этому заголовку можно **только для запросов из диапазонов вашего CDN**. Иначе любой желающий подставит произвольный IP в комментарии, логи и антиспам.

### 7. Геопривязка записей

Географические DNS-записи работают по принципу «ближайшая точка», а не по правилу «страна == RU». Поэтому мало завести две ветки — нужны геоякоря:

- ветка РФ — якорь в Москве;
- ветка мира — несколько якорей: Франкфурт, Нью-Йорк, Сингапур, Сан-Паулу.

Без мировых якорей посетители из Финляндии, Польши, Турции окажутся географически «ближе» к Москве и уйдут на origin в обход CDN. Абсолютной точности здесь не будет — это принципиальное ограничение GeoDNS, и его надо принять заранее.

### 8. Переезд зоны

Порядок, который экономит нервы:

1. Создать зону у нового DNS-провайдера как **точную копию** текущей — гео-сплит выключен, все ветки отдают origin.
2. Сверить записи один в один, запрашивая напрямую у новых NS.
3. Сменить NS у регистратора.
4. Дождаться распространения.
5. И только потом включать гео-сплит.

Два момента, о которых узнают поздно:

**TTL делегирования в зоне `.RU` — 345 600 секунд (4 суток)**, он задан реестром и не понижается:

```sh
dig NS example.ru @a.dns.ripn.net
```

Значит после смены NS резолверы до четырёх суток продолжат ходить на старого провайдера. Столько же займёт и откат. Старую зону всё это время держим живой и неизменной — это и есть кнопка отката.

**CAA-записи могут не переехать.** Cloudflare, например, синтезирует их на лету и не хранит в зоне: в экспорте их нет, а после переезда они просто исчезнут. Как минимум нужно завести:

```
example.ru.  300  IN  CAA  0 issue "letsencrypt.org"
```

Без этого CDN не выпустит сертификат для вашего домена, а хостинг — для origin-хостнейма.

### 9. Сертификат для домена на CDN

CDN обычно валидирует ваш домен по HTTP-01, а для этого домен уже должен указывать на pull zone. Возникает окно: NS переехали, сертификата ещё нет, а поставить домен на CDN без сертификата — TLS-ошибка у всех посетителей.

Чище выпустить сертификат заранее через DNS-01 (валидация TXT-записью, домен при этом никуда указывать не должен) и загрузить его в pull zone вручную. Окна без сертификата не возникает вовсе.

## Как проверить, что заработало

**Резолв из разных точек** — из РФ должен приходить origin, извне адреса CDN:

```sh
dig example.ru @77.88.8.8    # резолвер в РФ  → IP origin
dig example.ru @8.8.8.8      # резолвер вовне → IP PoP CDN
dig origin.example.ru        # везде          → IP origin
```

**Кэш отрабатывает** — второе обращение из-за рубежа должно дать `HIT`:

```sh
curl -sI https://example.ru/ | grep -i 'cdn-cache\|cache-control'
```

**Исключения не кэшируются:**

```sh
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 в инфраструктуре](https://wpcraft.ru/kb/infrastructure/tools.md)
- [CDN и производительность — сервисы](https://wpcraft.ru/kb/practices/web/tools/cdn-performance.md)
- [Сервисы веб-защиты](https://wpcraft.ru/kb/practices/web/security/web-protection-services.md)
- [Кэш страниц WordPress](https://wpcraft.ru/kb/stacks/wordpress/cache/page-cache.md)
- [Инфраструктурный уровень кэша](https://wpcraft.ru/kb/stacks/wordpress/cache/server-level-cache.md)
- [Домен и DNS](https://wpcraft.ru/kb/infrastructure/domain.md)

## Материалы и источники

- Практика внедрения на wpcraft.ru, 2026
- [Bunny.net](https://bunny.net)
- [Документация Bunny](https://docs.bunny.net)