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

141 lines
19 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
date: 2026-07-17
tags:
- xray
- xhttp
- splithttp
- transport
- cdn
- censorship
aliases:
- XHTTP
- SplitHTTP
- Xray XHTTP transport
link: https://github.com/XTLS/Xray-core/tree/main/transport/internet/splithttp
---
# 📡 XHTTP: транспорт Xray, притворяющийся обычным веб-трафиком
> [!info] О чём заметка
> Подробный разбор транспорта **XHTTP** (раньше назывался **SplitHTTP**) в [[xray/project-x|Xray-core]] — как он заворачивает прокси-трафик в обычные HTTP-запросы, чтобы пройти сквозь CDN вроде Cloudflare, и в чём разница между его режимами `packet-up`, `stream-up` и `stream-one`. Разбор основан на чтении исходного кода `transport/internet/splithttp/` (внутреннее имя пакета осталось `splithttp`, публичное название — XHTTP). XHTTP — это транспорт (как переносить), он ортогонален протоколу [[xray/vless|VLESS]] и слою безопасности [[xray/reality|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/reality|REALITY]] → `stream-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|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|XTLS-Vision]] работает только поверх прямого TLS/REALITY по TCP, поэтому в связке с XHTTP flow `xtls-rprx-vision` не использовался, и приходилось выбирать один подход к маскировке из двух. С сентября 2025 это изменилось: если включить [[xray/vless-encryption|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|карте слоёв VLESS-стека]].
## Ключевые параметры конфига
| Параметр | Что это |
|---|---|
| `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|Протокол VLESS]] — протокол, который переносится поверх XHTTP
- [[xray/vless-encryption|VLESS Encryption]] — шифрование самого VLESS: прячет UUID и адреса назначения от CDN и открывает Vision поверх XHTTP
- [[xray/vless-stack-map|Слои VLESS-стека]] — матрица: какие сочетания транспорта, `security`, `flow` и `encryption` работают
- [[xray/reality|REALITY]] — слой безопасности, с которым XHTTP работает через CDN
- [[xray/xtls-vision|XTLS и Vision]] — альтернативный подход (прямой TLS вместо HTTP-маскировки)
- [[xray/project-x|Project X (Xray-core)]] — обзор проекта и всех технологий
- 🔗 [transport/internet/splithttp](https://github.com/XTLS/Xray-core/tree/main/transport/internet/splithttp) — исходный код транспорта
---
> [!quote] 🤖 Эти статьи открыты — можно обучать на них ИИ
> При желании вы можете натренировать ИИ на наших статьях. Исходное форматирование и скачивание всего репозитория одним zip-архивом доступны в Forgejo: [исходник этой заметки](https://git.zapret.moe/zapretdiscordyoutube/todo/src/branch/main/xray/xhttp.md) · [весь репозиторий](https://git.zapret.moe/zapretdiscordyoutube/todo/src/branch/main).