Контекст: зачем XHTTP заводят за whitelisted-CDN
Российские мобильные операторы при блокировках и локальных шатдаунах переводят мобильный интернет в режим «белого списка»: доступны только whitelisted-ресурсы — часть CDN-сетей, госсайты, отдельные сервисы. VPN-нода, к которой клиент подключается напрямую, в этом режиме перестаёт отвечать: её IP не в белом списке.
Решение на уровне инфраструктуры — увести вход сервиса за whitelisted CDN-edge. Для оператора связи трафик выглядит как обычное обращение к «белому» CDN, а CDN уже проксирует запрос на вашу ноду (origin). Это вопрос устойчивости сервиса под ограничениями сети, а не отдельной пользовательской функции.
Ключевая оговорка: whitelist привязан не к домену, а к подсетям /24. У одних хостеров подсети попадают в белые списки, у других — нет, и статус со временем меняется. Selectel часто оказывается whitelisted, cloud.ru/SberCloud — часто нет. Универсального ответа не существует: каждую /24 нужно проверять актуально и без расчёта на гарантии.
Под троттлингом важен и выбор транспорта. Reality и Hysteria2 напрямую edge обычно не пропускает — их профиль не похож на легитимное обращение к CDN. XHTTP в режиме packet-up через CDN проходит: он идёт поверх обычного HTTP и не выделяется на фоне легитимного трафика к whitelisted-edge.
Как XHTTP передаёт данные поверх HTTP
XHTTP маскирует туннель под обычный веб-трафик и разделяет два направления:
- downlink (сервер → клиент) — длинный потоковый ответ на GET-запрос;
- uplink (клиент → сервер) — данные, которые клиент отправляет наверх.
В режиме packet-up uplink уходит на сервер отдельными POST-запросами. И здесь возникает ключевой инженерный вопрос: *куда именно* внутри HTTP-запроса положить полезную нагрузку uplink. Технически вариантов несколько — тело запроса (body), кастомный заголовок, query-параметр, cookie. При прямом соединении «клиент → нода» работает почти любой: запрос доходит до origin в неизменном виде.
Проблема появляется ровно в тот момент, когда между клиентом и нодой встаёт CDN. Отсюда и типичный симптом «xhttp не работает через cdn»: напрямую всё поднималось, а через edge — рвётся. Причина — в том, какие части HTTP-запроса CDN обязан донести до origin, а какие может свободно менять.
Главный тезис: почему заголовок ломается за CDN
Кастомными заголовками CDN-edge распоряжается по своему усмотрению: он может вырезать неизвестные ему заголовки, переписать или объединить их, ограничить суммарный размер, нормализовать регистр. Гарантии, что произвольный X-Payload доедет до origin в исходном виде, нет — и на практике он до ноды не доходит.
Если uplink-нагрузка лежит в таком заголовке, отказ выглядит обманчиво: TLS-хендшейк и downlink проходят, туннель формально поднимается «наполовину», но нода не получает поток от клиента. Результат — обрыв с характерной ошибкой unexpected EOF.
Тело запроса (body) — единственная часть, которую CDN обязан передать на origin как есть. Проксирование тела POST — базовая функция любого reverse-proxy/CDN, иначе через него не работал бы ни один аплоад. Поэтому единственно надёжное размещение uplink за CDN:
"uplinkDataPlacement": "body"
Момент неочевидный, но решающий: он не всплывает при тестах напрямую и проявляется только после включения CDN — что и сбивает инженеров с толку.
Карта размещения данных: что и куда класть
Правильное распределение частей запроса делает туннель устойчивым к проксированию. Ориентир:
| Данные | Куда класть | Почему так |
|---|---|---|
| uplink payload | body (uplinkDataPlacement: "body") | тело POST CDN проксирует на origin без изменений |
| session id / seq | cookie | cookie доходят до origin и привязывают серию запросов к одной сессии |
| padding | query-параметр | не влияет на семантику запроса, помогает варьировать размеры |
| маршрутизация | Host / SNI = CDN-поддомен | edge направляет запрос на нужный origin по домену |
Логика простая: всё, что критично для целостности туннеля, кладётся туда, что CDN сохраняет как есть (body, cookie, path, host). Всё, что не критично или служит маскировкой (padding), — туда, где потери допустимы. Кастомные заголовки под полезную нагрузку не используются вообще.
Практика: настройка ноды и клиента
За CDN нода держит обычный XHTTP-инбаунд (например, на порту 25454), а клиентский конфиг в качестве Address/SNI/Host указывает CDN-поддомен вместо IP ноды. Подход одинаково применим к панелям Remnawave, 3x-ui, Marzban, Hiddify — на уровне транспорта разницы нет.
Критично: path и все extra-параметры на клиенте и на ноде должны совпадать точь-в-точь. Любое расхождение — лишний слэш, другой регистр, лишний ключ в extra — и туннель не поднимется, даже если body-uplink настроен верно. Это вторая по частоте причина «не работает через CDN» после header-uplink.
По безопасности origin: закройте ноду файрволом на вход только с IP gateway и добавьте секрет-заголовок X-Cdn-Auth, который edge подставляет к каждому запросу. Так origin отвергает прямые обращения мимо CDN и не светит XHTTP-инбаунд наружу.
Отдельно про учёт трафика: множитель потребления ноды и per-user лимиты — это механизмы панели (в Remnawave, например). Множитель 0 на ноде означает, что её трафик не считается против лимита пользователя, хотя сама нода при этом работает штатно.
Экономика: body ещё и дешевле, чем header
Выбор body — не только про надёжность, но и про стоимость. XHTTP по своей природе плодит много мелких HTTP-запросов, а на масштабе CDN тарифицирует не только гигабайты, но и число запросов.
Прайс за гигабайт — только половина счёта. Вторая половина у части провайдеров это запросы, а XHTTP в режиме packet-up дробит аплинк на множество мелких обращений. Чем мельче и чаще uplink-запросы, тем выше доля «поштучной» платы за запросы в итоговом чеке.
Крупные POST в body позволяют укрупнять uplink и снижать число запросов — а значит, и стоимость. Header-based фрагментация, наоборот, обычно ведёт к более мелким и частым запросам: платите больше и получаете более хрупкий туннель. То есть uplinkDataPlacement: "body" выигрывает по обоим критериям сразу.
Диагностика частых ошибок
Короткий чек-лист, когда «через CDN не поднимается»:
unexpected EOF— почти всегда uplink уехал в заголовок вместо body, либо рассинхрон path/extra. ПроверьтеuplinkDataPlacement: "body"в первую очередь.- Напрямую работает, через CDN — нет — классический признак header-uplink: заголовок не доходит до origin.
- Туннель не поднимается совсем — сверьте
pathиextraна клиенте и ноде символ в символ. - Origin принимает запросы мимо CDN — не настроен firewall на IP gateway или отсутствует
X-Cdn-Auth.
Отдельно стоит развести две разные задачи. «Пробить троттлинг» (провести туннель в режиме белого списка) и «разблокировать стриминг» — это не одно и то же. Сервисы вроде Kinopoisk банят датацентр-IP по репутации: для российского контента нужен резидентный IP, и whitelist-CDN эту задачу не решает — это отдельный слой инфраструктуры.
Если не хочется самому подбирать whitelisted-подсети и следить за их статусом, whitelisted-вход можно взять как сервис. Clearway (clear-way.pro) даёт VPN-операторам «белый» вход перед их нодами через CDN-edge (*.whitechannel-x1-cdn.ru) — без продажи серверов, вы остаётесь на своей панели и своих нодах. Но правило про body действует в любом случае: за любым CDN uplink должен идти в теле запроса.