Блог Clearway

Почему uplink в XHTTP должен идти в body, а не в заголовок

Коротко

В XHTTP-транспорте, проксируемом через CDN, uplink-данные нужно передавать в теле HTTP-запроса — uplinkDataPlacement: "body", — а не в кастомном заголовке вроде X-Payload. CDN-edge не обязан доносить произвольные заголовки до origin: он вправе их резать, объединять или ограничивать по размеру, поэтому нода не получает поток клиента и туннель рвётся с ошибкой «unexpected EOF». Session и seq размещайте в cookie, padding — в query-параметре. Тело POST — единственная часть запроса, которую CDN проксирует на origin без изменений, поэтому за whitelisted-CDN uplink стабильно проходит только в body.

Контекст: зачем 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 payloadbody (uplinkDataPlacement: "body")тело POST CDN проксирует на origin без изменений
session id / seqcookiecookie доходят до origin и привязывают серию запросов к одной сессии
paddingquery-параметрне влияет на семантику запроса, помогает варьировать размеры
маршрутизация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 должен идти в теле запроса.

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

Что такое uplinkDataPlacement и какие у него значения?

Это параметр XHTTP-транспорта, определяющий, в какой части HTTP-запроса передавать uplink-данные клиента. Практически значимы два варианта: body (тело запроса) и размещение в кастомном заголовке. За CDN допустим только body — заголовок edge не гарантирует доставить до origin.

Почему напрямую всё работает, а через CDN туннель рвётся?

При прямом соединении запрос доходит до ноды в неизменном виде, поэтому даже header-uplink проходит. CDN-edge же вправе резать и переписывать кастомные заголовки, и uplink в заголовке до origin не доезжает. Отсюда обрыв с unexpected EOF именно после включения CDN.

Можно ли пустить за CDN Reality или Hysteria2 вместо XHTTP?

Под троттлингом и в режиме белого списка — обычно нет. Edge пропускает трафик, похожий на легитимное обращение к CDN, а профиль Reality/Hysteria2 напрямую он, как правило, не пропускает. Рабочий транспорт за whitelisted-CDN — XHTTP в режиме packet-up.

Как выбрать хостера, чтобы подсеть была whitelisted?

Whitelist привязан к подсетям /24, а не к хостеру целиком, и статус со временем меняется. Selectel часто оказывается в белых списках, cloud.ru/SberCloud — часто нет. Проверяйте каждую конкретную /24 актуально и не рассчитывайте на гарантии.

Решает ли whitelist-CDN разблокировку Kinopoisk и другого стриминга?

Нет. Whitelist-CDN помогает провести туннель в режиме белого списка, но стриминговые сервисы банят датацентр-IP по репутации. Для российского контента нужен резидентный IP — это отдельная задача, которую CDN-вход не закрывает.

Почему размещение в body ещё и снижает стоимость трафика?

XHTTP порождает много мелких запросов, а CDN на масштабе тарифицирует и число запросов, не только гигабайты. Считать по прайсу за гигабайт нельзя: CDN тарифицирует ещё и количество запросов, а XHTTP плодит много мелких обращений — итоговый счёт расходится с номиналом ₽/ГБ тем сильнее, чем интерактивнее трафик абонентов. Крупные POST в body укрупняют uplink, снижают число запросов и итоговый кост.

Whitelisted-вход для вашего VPN-сервиса

Clearway даёт «белый» CDN-вход перед вашей нодой — устойчивый к троттлингу операторов. Первые 10 ГБ бесплатно, без карты.

Попробовать →