todo/xray/xhttp.md
loop-uh 482ef21393
All checks were successful
Published content check / validate (push) Successful in 3s
Сохранить локальные статьи о FIDO и VLESS
Добавить новый раздел о стандартах FIDO, статьи о VLESS Encryption и слоях VLESS, а также несжатую иллюстрацию диагностики MTProxy. Обновить связанные материалы и направить ссылки на исходники новых статей в Forgejo.
2026-08-07 15:52:30 +03:00

19 KiB
Raw Permalink Blame History

date tags aliases link
2026-07-17
xray
xhttp
splithttp
transport
cdn
censorship
XHTTP
SplitHTTP
Xray XHTTP transport
https://github.com/XTLS/Xray-core/tree/main/transport/internet/splithttp

📡 XHTTP: транспорт Xray, притворяющийся обычным веб-трафиком

[!info] О чём заметка Подробный разбор транспорта XHTTP (раньше назывался SplitHTTP) в xray/project-x — как он заворачивает прокси-трафик в обычные HTTP-запросы, чтобы пройти сквозь CDN вроде Cloudflare, и в чём разница между его режимами packet-up, stream-up и stream-one. Разбор основан на чтении исходного кода transport/internet/splithttp/ (внутреннее имя пакета осталось splithttp, публичное название — XHTTP). XHTTP — это транспорт (как переносить), он ортогонален протоколу xray/vless и слою безопасности xray/reality.

TL;DR

  • XHTTP — транспорт, который упаковывает поток прокси в обычные HTTP GET/POST-запросы. Для CDN и наблюдателя это выглядит как штатное веб-приложение.
  • «Split» в старом названии = разделение направлений: скачивание (download) и отправка (upload) идут разными HTTP-запросами, потому что HTTP по своей природе полудуплексный, и промежуточные узлы (CDN, nginx) плохо переносят долгие двунаправленные потоки.
  • Три режима: packet-up (аплинк дробится на кучу коротких POST — самый CDN-дружелюбный), stream-up (аплинк одним длинным POST, даунлинк отдельным GET), stream-one (всё в одном дуплексном запросе — для REALITY напрямую, без CDN).
  • XMUX мультиплексирует много логических сессий в одно физическое соединение и ротирует соединения — как настоящий браузер.
  • Маскировка усиливается паддингом, заголовками «под fetch/gRPC/SSE» и анти-буферными заголовками, отключающими кэширование на CDN.

Зачем нужен XHTTP: пройти сквозь CDN

Основная задача XHTTP — прогнать прокси-трафик через CDN (сеть доставки контента, например Cloudflare). Смысл в том, что цензору очень дорого блокировать CDN целиком: за одним IP Cloudflare стоят миллионы легальных сайтов. Если прокси-трафик неотличим от обычных запросов к сайту за CDN, заблокировать его точечно почти невозможно.

Но CDN — капризная среда: она понимает только валидный HTTP, не любит долгие двунаправленные соединения, буферизует и кэширует ответы. Поэтому нельзя просто пустить туда произвольный TCP-туннель. XHTTP устроен так, чтобы выглядеть как настоящее веб-приложение, которое грузит данные POST-запросами и получает ответ GET-запросом.

Проще говоря: XHTTP переодевает прокси-туннель в костюм обычного сайта, да так тщательно, что даже придирчивый охранник-CDN пропускает его как своего.

«Split»: почему upload и download — разные запросы

Один логический дуплексный туннель (VLESS поверх транспорта) в коде представлен структурой splitConn — это соединение с раздельными reader (входящий поток) и writer (исходящий). Название «Split» отражает главную идею: два направления физически разнесены на разные HTTP-запросы.

Причина в природе HTTP: тело запроса естественно течёт вверх (клиент → сервер), тело ответа — вниз. Полноценный одновременный дуплекс в одном HTTP-обмене возможен только по HTTP/2 или HTTP/3 и плохо переживает промежуточные прокси. Поэтому:

  • Download (сервер → клиент) — один длинный GET, тело ответа которого стримится бесконечно.
  • Upload (клиент → сервер) — либо одно длинное тело POST, либо множество коротких POST (зависит от режима).

Сессию на сервере склеивает общий sessionId: GET-запрос даунлинка и все POST-запросы аплинка с одинаковым sessionId относятся к одному туннелю.

Три режима работы

Режим задаётся полем mode. Значение auto (по умолчанию) раскрывается так: обычно → packet-up; при включённом xray/realitystream-one; а если при REALITY задан отдельный канал скачивания (downloadSettings) → stream-up.

packet-up — самый CDN-дружелюбный

Аплинк нарезается на множество коротких независимых POST-запросов, каждый несёт кусок данных с порядковым номером seq. Даунлинк — отдельный длинный GET.

Почему это лучше всего проходит через CDN: короткие POST + один GET не требуют полного дуплекса, который многие CDN не поддерживают. Но есть сложность — независимые POST приходят на сервер в произвольном порядке (через CDN они могут идти по разным бэкенд-соединениям). Поэтому каждый POST помечен монотонным seq, а сервер восстанавливает исходный порядок.

За пересборку отвечает upload_queue.go — очередь с приоритетом (min-heap по seq): пакеты выдаются наверх строго по возрастанию номера, «опоздавшие» копятся в куче и ждут недостающий. Если куча разрастается сверх лимита (scMaxBufferedPosts, по умолчанию 30) — соединение рвётся, чтобы потерянный пакет не съел всю память.

На стороне клиента есть важная оптимизация — батчинг: множество мелких Write склеиваются в буфере в один POST покрупнее (иначе пропускная способность резко падает). Между POST выдерживается пауза scMinPostsIntervalMs (по умолчанию 30 мс) — заодно сбивает фингерпринт по таймингу.

stream-up — аплинк потоком

Аплинк идёт одним длинным потоковым POST (тело не закрывается, данные текут), даунлинк — отдельным GET. Может использовать разные адреса и транспорты для отправки и скачивания (через downloadSettings). Это разделение «загружаю через один канал, качаю через другой».

stream-one — всё в одном запросе

И аплинк, и даунлинк идут в рамках одного HTTP-обмена: тело запроса — вверх, тело ответа — вниз, одновременно. Это истинный дуплекс, требующий HTTP/2 или HTTP/3. Идеален для REALITY напрямую (без CDN), где полный дуплекс гарантирован. sessionId здесь не нужен — сессия и есть один запрос.

Режим Uplink Downlink Где хорош
packet-up много коротких POST с seq отдельный длинный GET через CDN, HTTP/1.1
stream-up одно длинное тело POST отдельный GET раздельные каналы up/down
stream-one тело одного запроса тело того же ответа REALITY напрямую, h2/h3

XMUX: мультиплексирование как у браузера

XMUX управляет тем, сколько HTTP-запросов туннеля мультиплексируется в одно нижележащее (TCP/QUIC) соединение и когда соединения переиспользуются или закрываются. Смысл — вести себя как настоящий браузер: тот держит несколько долгих HTTP/2-соединений и гоняет по ним много параллельных запросов, периодически их обновляя.

Ключевые лимиты (все задаются диапазонами и рандомизируются):

  • maxConcurrency — сколько запросов одновременно активны на одном соединении.
  • maxConnections — сколько соединений держать в пуле.
  • cMaxReuseTimes — сколько раз одно соединение можно переиспользовать.
  • hMaxRequestTimes — сколько всего запросов провести через соединение, прежде чем сменить его.
  • hMaxReusableSecs — временной лимит жизни соединения.

Когда соединение исчерпало лимит запросов или время жизни — клиент переключается на свежее. Ротация соединений экономит TLS-рукопожатия и одновременно мешает фингерпринтить прокси по аномально долгоживущим соединениям.

Маскировка: паддинг и «правильные» заголовки

XHTTP старательно имитирует легитимный HTTP-трафик несколькими способами:

  • XPadding — случайный заполнитель (xPaddingBytes, по умолчанию 1001000 байт) размывает характерные размеры запросов и ответов. Есть даже режим tokenish, который подгоняет паддинг под нужную длину после сжатия заголовков HPACK/QPACK, чтобы на проводе он занимал ровно заданное число байт. По умолчанию паддинг прячется в query-параметр x_padding внутри заголовка Referer.
  • Заголовки под браузер — запросы по умолчанию имитируют браузерный fetch.
  • Маскировка под gRPC — потоковые запросы помечаются Content-Type: application/grpc.
  • Маскировка под SSE — даунлинк-ответ помечается Content-Type: text/event-stream (Server-Sent Events).
  • Анти-буферные заголовкиX-Accel-Buffering: no и Cache-Control: no-store отключают буферизацию и кэширование на nginx/CDN. Это критично: иначе CDN накопил бы стрим в кэше и сломал интерактивность.

Метаданные (sessionId, seq) и даже саму полезную нагрузку XHTTP умеет раскладывать по разным частям запроса — в путь URL, query, заголовки или cookie (настраивается через sessionIDPlacement, seqPlacement, uplinkDataPlacement). При размещении данных в заголовках/cookie они кодируются base64url и режутся на чанки. Это даёт гибкость под требования конкретного CDN.

Как XHTTP сочетается с TLS и REALITY

XHTTP — это транспорт уровня приложения (HTTP), а шифрование задаётся отдельно, в общих настройках потока (streamSettings), а не внутри самого XHTTP. Слой безопасности — обычный TLS (через uTLS с фингерпринтом браузера) или xray/reality.

Выбор версии HTTP зависит от слоя безопасности: при REALITY — всегда HTTP/2; без TLS — HTTP/1.1; иначе по согласованному ALPN (h3 → HTTP/3, иначе HTTP/2). Именно поэтому при REALITY автоматический режим — stream-one (полный дуплекс гарантирован), а через произвольный CDN по HTTP/1.1 безопаснее packet-up.

[!note] XHTTP и Vision: раньше не совмещались, теперь совмещаются через VLESS Encryption Долгое время правило звучало просто: xray/xtls-vision работает только поверх прямого TLS/REALITY по TCP, поэтому в связке с XHTTP flow xtls-rprx-vision не использовался, и приходилось выбирать один подход к маскировке из двух. С сентября 2025 это изменилось: если включить xray/vless-encryption, Vision становится доступен поверх XHTTP и других транспортов — документация Xray описывает XTLS как доступный либо при TCP + TLS/REALITY, либо при VLESS Encryption без ограничений на транспорт. Оговорка: ядерный splice через XHTTP всё равно не включится — он работает только поверх голого TCP. Остальное Vision делает исправно: и набивку внутреннего рукопожатия, и отказ от повторного шифрования уже зашифрованного TLS 1.3, так что выигрыш по процессору сохраняется и здесь. Рекомендация автора Xray для связок через CDN звучит как «VLESS Encryption + XTLS Vision + XHTTP XMUX». Разбор комбинаций — в xray/vless-stack-map.

Ключевые параметры конфига

Параметр Что это
mode Режим: auto / packet-up / stream-up / stream-one
path Базовый URL-путь запросов
host Значение заголовка Host / SNI
headers Дополнительные HTTP-заголовки
scMaxEachPostBytes Макс. размер тела одного upload-POST (по умолчанию 1 000 000)
scMinPostsIntervalMs Мин. пауза между POST (по умолчанию 30 мс)
scMaxBufferedPosts Глубина очереди пересборки (по умолчанию 30)
xPaddingBytes Диапазон длины паддинга (по умолчанию 1001000)
noGRPCHeader / noSSEHeader Отключить маскировку под gRPC / SSE
xmux Настройки мультиплексирования (см. выше)
downloadSettings Отдельный транспорт/адрес для канала скачивания

📚 См. также

  • xray/vless — протокол, который переносится поверх XHTTP
  • xray/vless-encryption — шифрование самого VLESS: прячет UUID и адреса назначения от CDN и открывает Vision поверх XHTTP
  • xray/vless-stack-map — матрица: какие сочетания транспорта, security, flow и encryption работают
  • xray/reality — слой безопасности, с которым XHTTP работает через CDN
  • xray/xtls-vision — альтернативный подход (прямой TLS вместо HTTP-маскировки)
  • xray/project-x — обзор проекта и всех технологий
  • 🔗 transport/internet/splithttp — исходный код транспорта

[!quote] 🤖 Эти статьи открыты — можно обучать на них ИИ При желании вы можете натренировать ИИ на наших статьях. Исходное форматирование и скачивание всего репозитория одним zip-архивом доступны в Forgejo: исходник этой заметки · весь репозиторий.