Три вещи, без которых схема не поедет
- GET вместо POST на uplink. xHTTP packet-up по умолчанию шлёт исходящие пакеты методом POST — многие российские CDN режут POST, отдавая 403. Решение — перевести uplink на GET (
uplinkHTTPMethod: GET) и спрятать данные в заголовки/cookie/query.
- Правильная сеть CDN. Часть российских CDN блокирует такой трафик прямо на edge (403 с заголовком вида
x-cdn-edge-cache: HIT), и это не лечится из панели CDN. Yandex Cloud CDN — другая edge-сеть, она такой трафик пропускает.
- Короткий путь. Yandex CDN нормально проксирует односегментные пути (
/poll), а многосегментные (/api/v4/media/session/poll) режет собственным 404. Путь должен быть коротким.
Что нужно заранее
- VPS с публичным IP (origin) — подойдёт любой провайдер
- Свой домен с управляемым DNS:
origin.example.com → A-запись на IP origin (DNS only, без проксирования), cdn.example.com → будет CNAME на Yandex CDN
- На origin — nginx, Xray, выпущенный Let's Encrypt сертификат на origin.example.com
- Аккаунт в Yandex Cloud с включённым Cloud CDN
Шаг 1. nginx на origin
upstream xray_xhttp_get {
server 127.0.0.1:11443;
keepalive 256;
}
server {
listen 443 ssl http2;
server_name origin.example.com;
ssl_certificate /etc/letsencrypt/live/origin.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/origin.example.com/privkey.pem;
client_max_body_size 0;
client_body_timeout 900s;
location / {
proxy_pass http://xray_xhttp_get;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header Connection "";
proxy_buffering off;
proxy_request_buffering off;
proxy_cache off;
add_header X-Accel-Buffering "no" always;
proxy_read_timeout 900s;
}
}
proxy_buffering off и proxy_request_buffering off здесь критичны — без них долгоживущий xHTTP-стрим будет рваться.
Шаг 2. Xray inbound
Главное в inbound-е — mode: packet-up, короткий path: "/poll" и блок extra с явным GET-uplink:
{
"tag": "cdn-get-inbound",
"port": 11443,
"listen": "127.0.0.1",
"protocol": "vless",
"settings": { "clients": [], "decryption": "none" },
"streamSettings": {
"network": "xhttp",
"security": "none",
"xhttpSettings": {
"mode": "packet-up",
"path": "/poll",
"extra": {
"uplinkHTTPMethod": "GET",
"sessionKey": "media_sid",
"sessionPlacement": "cookie",
"uplinkDataKey": "X-Playback-Token",
"uplinkDataPlacement": "header",
"seqKey": "offset",
"seqPlacement": "query"
}
}
}
}
Важно: этот блок extra должен совпадать в двух местах — в конфиге сервера и в настройках Host на клиентской стороне (там path указывается отдельным полем, дублировать его внутри extra не нужно). Для GET-uplink нужно достаточно свежее ядро Xray и на сервере, и в клиенте — на старых ядрах поля вида uplinkHTTPMethod просто игнорируются, и клиент откатывается обратно на POST.
Шаг 3. Сертификат на cdn.example.com
Так как HTTPS клиенту отдаёт сам CDN, сертификат нужен именно на CDN-домен: в Yandex Cloud Certificate Manager выпускается Let's Encrypt-сертификат с проверкой через DNS, Yandex выдаёт CNAME для валидации вида _acme-challenge.cdn.example.com → добавить эту запись у своего DNS-провайдера (DNS only, без проксирования) и дождаться статуса «Выпущен». Запись _acme-challenge после выпуска не удалять — она нужна для автопродления.
Шаг 4. Ресурс Yandex Cloud CDN
При создании ресурса CDN важны следующие настройки:
- Источник — origin.example.com, протокол HTTPS, заголовок Host — своё значение origin.example.com
- Проверка сертификата источника — выключена, SNI-хост — включён, origin.example.com
- Сертификат — выпущенный на предыдущем шаге, профиль TLS — TLSv1.2+
- Кеширование в CDN и в браузере — выключено полностью (CDN должен прозрачно пробрасывать трафик, а не кэшировать)
- Игнорировать cookie и query-параметры — оба флага сняты (cookie несёт сессию, query несёт offset)
- Разрешённые методы — GET (можно добавить HEAD, OPTIONS)
- Экранирование (shielding) — выключено, промежуточный кэш-слой вредит стриму
Шаг 5. DNS на CDN-домен
После создания ресурса Yandex покажет CNAME-таргет вида <id>.topology.gslb.yccdn.ru — на него и указывается CNAME-запись для cdn.example.com, обязательно без проксирования на стороне DNS-провайдера (это критично, «оранжевое облако» у Cloudflare, к примеру, всё сломает). Проверить резолв:
dig +short cdn.example.com
# должно вести на yccdn.ru и в итоге на IP 188.72.x (Yandex edge)
Шаг 6. Настройка Host на клиентской стороне
- Address / SNI / Host: cdn.example.com
- Port: 443
- Path: /poll
- Mode: packet-up, ALPN: h2, http/1.1, fingerprint: chrome
- xHTTP extra — тот же блок, что в конфиге сервера (без поля path)
Проверка
curl не умеет в полноценную xHTTP-сессию и будет отдавать 400/404 — это нормально, реальный тест только через клиент. Признаки рабочего туннеля в логе origin: IP запросов начинается на 188.72.110.x / 188.72.111.x (edge Yandex), запросы GET /poll/?offset=... со статусом 200.
Диагностика по слоям, если не работает
# доходит ли запрос через CDN до origin:
curl -k -s -D - -o /dev/null "https://cdn.example.com/poll?offset=0"
# отвечает ли origin напрямую, минуя CDN:
curl -k -s -D - -o /dev/null --resolve origin.example.com:443:<ORIGIN_IP> \
"https://origin.example.com/poll?offset=0"
403 с заголовком вида x-cdn-edge-cache: HIT и пустой лог на origin — значит CDN блокирует трафик прямо на edge, для этой схемы такая CDN-сеть не подходит. 404 от CDN при пустом логе origin — скорее всего путь не односегментный. Если origin отвечает 400 с заголовком вида x-rewrite-url при прямом обращении — сервер исправен, и проблема именно в CDN или в клиенте.