XHTTP: Beyond REALITY
XHTTP — нативный транспорт HTTP с тремя режимами (packet-up, stream-up и stream-one). Он проходит через большинство HTTP-совместимых промежуточных узлов, включая CDN и обратные прокси, а также изначально поддерживает QUIC H3 через CDN.
Он реализует настоящее разделение исходящего и входящего потоков, padding заголовков, мультиплексирование XMUX и подходит для самых разных схем развёртывания.
TIP
XHTTP по умолчанию использует мультиплексирование. Задержка ниже, чем у Vision, но многопоточный тест скорости может показать худший результат, если перед тестом не установить "maxConcurrency": 1.
DANGER
Не включайте mux.cool вместе с XHTTP. Новые серверы Xray проверяют это и принимают только чистый XUDP.
Быстрый старт
Как для TLS, так и для REALITY обычно достаточно указать только path:
- Укажите только
path, остальные параметры оставьте пустыми. - Если сервер поддерживает QUIC H3, задайте клиентский
alpnравным"h3". - При использовании оптимального IP CDN укажите IP в
address, а домен — вserverName(SNI). - Если не удаётся подключиться через Cloudflare, включите поддержку gRPC в панели Cloudflare.
- Если соединение не проходит через Nginx, замените
proxy_passнаgrpc_pass. - При несовместимости с другим CDN или обратным прокси выберите
"packet-up"— наиболее совместимый режим.
WARNING
Некоторые CDN разрывают HTTP-соединения, если долго нет фактических данных в направлении загрузки. При использовании Cloudflare для долгоживущих сеансов, например SSH, настройте также keepalive на уровне приложения: ClientAliveInterval в sshd_config SSH-сервера либо ServerAliveInterval на SSH-клиенте. Не полагайтесь только на keepalive транспорта XHTTP.
XHTTPObject
XHTTPObject используется в поле xhttpSettings объекта StreamSettingsObject.
{
// пример outbound; те же настройки применимы к inbound
"outbounds": [
{
// ...
"streamSettings": {
"method": "xhttp",
"xhttpSettings": {
"host": "example.com",
"path": "/yourpath",
"mode": "auto",
"extra": {
"headers": {
"Key": "Value"
},
"xPaddingBytes": "100-1000",
"noGRPCHeader": false,
"noSSEHeader": false,
"scMaxEachPostBytes": 1000000,
"scMinPostsIntervalMs": 30,
"scMaxBufferedPosts": 30,
"scStreamUpServerSecs": "20-80",
"xmux": {
"maxConcurrency": "16-32",
"maxConnections": 0,
"cMaxReuseTimes": 0,
"hMaxRequestTimes": "600-900",
"hMaxReusableSecs": "1800-3000",
"hKeepAlivePeriod": 0
},
"downloadSettings": {
"address": "",
"port": 443,
"network": "xhttp",
"security": "tls",
"tlsSettings": {},
"xhttpSettings": {
"path": "/yourpath"
},
"sockopt": {}
}
}
}
}
}
]
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
host: string
HTTP-заголовок Host, отправляемый клиентом. Приоритет на клиенте: host > serverName > address.
Если host задан на сервере, сервер проверяет совпадение значения клиента; иначе проверка не выполняется. Обычно поле лучше не задавать. Его нельзя помещать в headers. По умолчанию пусто.
path: string
Путь HTTP-запроса XHTTP. Значение по умолчанию — "/". Сервер связывает исходящий и входящий потоки по случайному UUID внутри этого пути.
mode: "auto" | "packet-up" | "stream-up" | "stream-one"
Режим транспорта. По умолчанию — "auto".
Поведение "auto" на клиенте:
- TLS H2 →
stream-up - REALITY →
stream-one(при наличииdownloadSettings→stream-up) - остальные случаи →
packet-up
Серверный "auto" принимает все три режима. Конкретный режим обычно принимает только себя; исключение — "stream-up", который также принимает stream-one.
| Режим | Исходящий поток | Входящий поток | Совместимость | Применение |
|---|---|---|---|---|
packet-up | Пакетные POST | Потоковый GET | Максимальная | CDN и различные промежуточные узлы |
stream-up | Потоковый POST | Потоковый GET | Высокая | TLS H2 через CDN, REALITY |
stream-one | Тело потокового POST | Ответ того же POST | Средняя | REALITY и узлы с двунаправленной потоковой передачей |
extra: object
Контейнер исходного JSON для всех параметров, кроме host, path и mode. При наличии extra действуют только четыре поля: host, path, mode и extra.
Ссылки общего доступа и GUI обычно содержат только эти четыре поля. Издатель сервиса должен подготовить и передать extra; клиенту не следует произвольно его менять.
Общие параметры extra
headers: map {string: string}
Только клиент. Пользовательские пары HTTP-заголовков. По умолчанию пусто.
xPaddingBytes: string | number
Размер padding заголовков, скрывающий их фиксированную длину. Клиент использует Referer: ...?x_padding=XXX..., сервер — X-Padding: XXX.... Размещение в Referer предотвращает лишние CORS preflight-запросы Browser Dialer. Сервер проверяет допустимый диапазон клиентского padding.
Диапазон, например "100-1000", выбирается случайно для каждого запроса или ответа. Значение по умолчанию — "100-1000".
Внешний вид HTTP и обфускация
Эти параметры изменяют представление padding, метаданных сеанса и исходящих данных в HTTP-запросах. Они предназначены прежде всего для совместимости с CDN и WAF, фильтрующими фиксированные признаки XHTTP. Настройки клиента и сервера должны быть совместимы. Они уменьшают число статических признаков, но не гарантируют нераспознаваемость трафика.
{
"xPaddingObfsMode": true,
"xPaddingKey": "_dc",
"xPaddingHeader": "Referer",
"xPaddingPlacement": "queryInHeader",
"xPaddingMethod": "tokenish",
"uplinkHTTPMethod": "POST",
"sessionPlacement": "path",
"sessionKey": "",
"seqPlacement": "path",
"seqKey": "",
"uplinkDataPlacement": "auto",
"uplinkDataKey": "X-Data",
"uplinkChunkSize": "3000-4000",
"serverMaxHeaderBytes": 0
}2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
xPaddingObfsMode: true | false
Включает настраиваемое представление padding. По умолчанию false, поэтому для совместимости со старыми клиентами сохраняется прежний вид Referer: ...?x_padding=XXX.... После включения внешний вид задают следующие параметры ключа, заголовка, размещения и метода.
xPaddingKey: string
Имя query-параметра или Cookie с padding. По умолчанию "x_padding".
xPaddingHeader: string
При размещении "queryInHeader" этот HTTP-заголовок содержит URL с padding в query. При "header" он содержит padding напрямую. По умолчанию "X-Padding"; запросы клиента в старом режиме по-прежнему используют Referer.
xPaddingPlacement: "queryInHeader" | "query" | "header" | "cookie"
Место размещения padding. По умолчанию "queryInHeader": padding помещается в query URL, а URL — в выбранный HTTP-заголовок. Остальные значения помещают его непосредственно в query запроса, HTTP-заголовок или Cookie.
xPaddingMethod: "repeat-x" | "tokenish"
Метод генерации padding. Совместимый режим по умолчанию "repeat-x" повторяет X. "tokenish" создаёт случайные символы, похожие на токен, и корректирует размер с учётом сжатия HTTP-заголовков.
uplinkHTTPMethod: string
HTTP-метод исходящих запросов. Регистр не важен, значение преобразуется в верхний. По умолчанию "POST". В зависимости от промежуточного узла можно использовать PUT, PATCH, GET или другой метод; GET доступен только в packet-up. Xray не проверяет и не чередует методы автоматически: выбранный метод должен проходить через все CDN и обратные прокси.
sessionPlacement: "path" | "query" | "header" | "cookie"
Место идентификатора сеанса. По умолчанию "path". Для других вариантов имя задаётся через sessionKey; без него используются x_session для query/cookie и X-Session для заголовка.
seqPlacement: "path" | "query" | "header" | "cookie"
Место порядкового номера packet-up. По умолчанию "path". Для других вариантов имя задаётся через seqKey; без него используются x_seq для query/cookie и X-Seq для заголовка.
uplinkDataPlacement: "auto" | "body" | "header" | "cookie"
Место исходящих данных. По умолчанию "auto": обычная отправка использует body, а сервер автоматически принимает поддерживаемые варианты. "header" и "cookie" доступны только в packet-up; данные кодируются Base64 и разбиваются на части. Эти режимы нужны для особых цепочек, где разрешён только GET или запрещено тело запроса, и не рекомендуются как обычная настройка.
uplinkDataKey: string
Базовое имя частей данных в заголовках или Cookie. По умолчанию x_data для Cookie и X-Data для header/auto.
uplinkChunkSize: number | string
Размер одной Base64-части при передаче данных в Header или Cookie. Для рандомизации поддерживаются диапазоны. Параметр ограничивает только одну часть; общий объём данных запроса по-прежнему задаёт scMaxEachPostBytes.
serverMaxHeaderBytes: number
Только сервер. Изменяет общий лимит заголовков запроса, принимаемый HTTP-сервером XHTTP. 0 использует значение реализации по умолчанию, отрицательные значения недопустимы. Увеличивайте его только при контроле всей цепочки: CDN или обратный прокси всё равно может применять меньший лимит.
WARNING
Передача через Header/Cookie увеличивает объём из-за Base64 и ограничена размером отдельного поля, всех заголовков, обратного прокси и CDN. При HTTP 431 уменьшите и uplinkChunkSize, и scMaxEachPostBytes. Рост числа запросов может потребовать увеличить серверный scMaxBufferedPosts. Одно лишь увеличение serverMaxHeaderBytes не меняет лимиты промежуточных узлов.
noGRPCHeader: true | false
Только клиент (stream-up / stream-one). Отключает маскировку исходящего потока Content-Type: application/grpc. По умолчанию false.
noSSEHeader: true | false
Только сервер. Отключает маскировку входящего потока Content-Type: text/event-stream. По умолчанию false. Если сочетание gRPC-запроса и SSE-ответа не проходит через промежуточный узел в режиме stream-one, попробуйте true.
Параметры packet-up
Здесь sc означает sub-connection. Ограничения считаются отдельно для каждого проксируемого соединения, даже если несколько соединений используют одно базовое соединение H2/H3.
scMaxEachPostBytes: number | string
Максимум данных в одном пакетном исходящем запросе клиента. Post сохранено в имени для совместимости; параметр действует также для PUT, PATCH и GET. Значение должно быть ниже лимита CDN; сервер отклоняет превышение. Поддерживает диапазоны, например "500000-1000000". По умолчанию 1000000 (1 МБ).
scMinPostsIntervalMs: number | string
Только клиент. Минимальный интервал между POST одного проксируемого соединения, в миллисекундах. Поддерживает диапазоны, например "10-50". По умолчанию 30.
scMaxBufferedPosts: number
Только сервер. Максимальное число буферизованных POST на одно проксируемое соединение; при превышении соединение закрывается. По умолчанию 30.
Параметры stream-up
scStreamUpServerSecs: number | string
Только сервер. Сервер периодически отправляет xPaddingBytes байт padding, чтобы CDN не закрыл неактивный входящий поток вместе с исходящим stream-up. Поддерживает диапазоны, например "20-80"; это же значение используется по умолчанию. -1 отключает механизм, при этом сервер может не отправлять заголовки ответа немедленно, как в старых версиях.
Параметры XMUX
Только клиент. Управляют мультиплексированием H2/H3.
WARNING
Если явно задано любое поле XMUX, остальные поля больше не получают автоматические значения по умолчанию — их также нужно указать явно.
maxConcurrency: number | string
Максимум одновременных проксируемых запросов в одном TCP/QUIC-соединении. После достижения лимита Xray открывает новое соединение. Конфликтует с maxConnections. Если все поля XMUX равны нулю, используется случайное "16-32".
maxConnections: number | string
Максимум одновременных базовых соединений. До достижения лимита каждый новый запрос открывает соединение, затем используются существующие. Конфликтует с maxConcurrency. По умолчанию 0 (без ограничения), поддерживает диапазоны.
cMaxReuseTimes: number | string
Максимальное число повторных использований соединения для новых проксируемых запросов. По умолчанию 0 (без ограничения), поддерживает диапазоны.
hMaxRequestTimes: number | string
Максимальное суммарное число HTTP-запросов в соединении. В Nginx по умолчанию 1000. Если все поля XMUX равны нулю, используется случайное "600-900", иначе — 0. Обычно stream-one создаёт один запрос, stream-up — два, packet-up — много. Подсчёт не абсолютно точен из-за повторов GET, поэтому не задавайте граничное значение промежуточного узла.
hMaxReusableSecs: number | string
Максимальное время повторного использования соединения в секундах. В Nginx по умолчанию один час. Если все поля XMUX равны нулю, используется случайное "1800-3000", иначе — 0.
hKeepAlivePeriod: number
Интервал клиентского keepalive для неактивных H2/H3-соединений. 0 означает 45 секунд Chrome H2 или 10 секунд quic-go H3. Отрицательное значение, например -1, отключает keepalive. Рекомендуется оставить 0. Это единственное поле XMUX без поддержки диапазонов.
maxConnections: 1 способствует использованию одного базового соединения. maxConcurrency: 1 создаёт больше базовых соединений при параллельной нагрузке и полезен для многопоточных тестов скорости. Настройки по умолчанию периодически заменяют основные H2/H3-соединения.
Разделение исходящего и входящего потоков
downloadSettings: object
Только клиент. Полный набор streamSettings для входящего потока с дополнительными address и port, указывающими другую точку входа.
{
"downloadSettings": {
"address": "",
"port": 443,
"network": "xhttp",
"security": "tls",
"tlsSettings": {},
"xhttpSettings": {
"path": "/yourpath"
},
"sockopt": {}
}
}2
3
4
5
6
7
8
9
10
11
12
13
network обязательно должен быть "xhttp". security может быть "tls" или "reality". Значение xhttpSettings.path должно совпадать в обоих направлениях.
sockopt можно передавать. penetrate: true в исходящем sockopt переопределяет параметры сокета входящего потока.
WARNING
Кроме исключения sockopt.penetrate, downloadSettings ничего не наследует от исходящего потока. Адрес, порт, TLS/REALITY, XHTTP и XMUX задаются независимо. Даже значения диапазонов XMUX по умолчанию выбираются отдельно для каждого направления.
Направления могут использовать разные адреса, SNI, IPv4/IPv6, H2/H3, CDN или точки REALITY. В итоге они должны прийти с одинаковым path в один XHTTP inbound на одном сервере.
Принцип работы
packet-up
- По умолчанию клиент отправляет данные через
POST /yourpath/sameUUID/seq.- Случайный UUID связывает оба направления.
- Если сервер не свяжет их за 30 секунд, сеанс завершается.
seqначинается с 0. Тело предыдущего POST должно быть отправлено полностью, но ждать ответ не нужно.- Сервер восстанавливает порядок POST, пришедших не по порядку.
- UUID и
seqпо умолчанию находятся в path. Настройки внешнего вида позволяют перенести их в query, Header или Cookie.
- Клиент запускает загрузку через
GET /yourpath/sameUUID.- Ответ содержит
X-Accel-Buffering: no,Cache-Control: no-storeиContent-Type: text/event-stream. - Для HTTP/1.1 также используется
Transfer-Encoding: chunked; H2/H3 он не нужен.
- Ответ содержит
- Ответы содержат
Access-Control-Allow-Origin: *иAccess-Control-Allow-Methods: GET, POST.
packet-up создаёт много POST, а padding в Referer увеличивает журналы обратного прокси. При отсутствии требований аудита настройте журналирование этих запросов.
stream-up
Заменяет пакетную отправку на потоковый POST /yourpath/sameUUID, сохраняя эффективность исходящего потока. По умолчанию используется Content-Type: application/grpc.
stream-one
Использует POST /yourpath/: тело запроса служит исходящим потоком, тело ответа — входящим. Завершающий / добавляется автоматически.
H1 / H2 / H3
Многие CDN и обратные прокси преобразуют клиентский H3 в H1 или H2 при обращении к origin, поэтому клиент H3 не требует прямого прослушивания H3 сервером XHTTP.
По умолчанию сервер слушает TCP и обрабатывает H1/H2. quic-go на UDP используется только при включённом TLS и единственном значении alpn — "h3". Обычно предпочтительнее скрыть XHTTP за настоящим Nginx или Caddy и поручить публичный H3 обратному прокси, чтобы уменьшить признаки реализации origin.
Поведение клиента:
- TLS/REALITY по умолчанию использует H2, иначе HTTP/1.1.
- Единственный
alpn"http/1.1"выбирает H1. - Единственный
alpn"h3"выбирает quic-go H3. - С Browser Dialer версию HTTP выбирает браузер, а
tlsSettingsне управляет ALPN или TLS-отпечатком этого соединения.
TIP
Установите уровень журнала "info", чтобы увидеть фактическую версию HTTP, Host, режим XHTTP и параметры разделения потоков.
Сравнение stream-up и gRPC
| Параметр | stream-up | Транспорт gRPC |
|---|---|---|
| Реализация | Без библиотеки gRPC, выше производительность | Требует библиотеку gRPC |
| Входящий трафик | Отдельный GET, не ограниченный лимитами CDN для gRPC | Подчиняется лимитам CDN |
| Дополнения | Header padding, XMUX, разделение потоков, передаваемый extra | Нет |
Устранение неполадок
| Проблема | Что проверить |
|---|---|
| Нет подключения через Cloudflare | Включите gRPC в панели Cloudflare |
| Nginx не передаёт потоковый исходящий трафик | Используйте grpc_pass, а не обычный proxy_pass |
| Другой CDN или прокси несовместим | Выберите "packet-up" |
stream-one ограничивается или отключается | Попробуйте "stream-up" либо серверный "noSSEHeader": true |
| Долгоживущее соединение разрывается | Проверьте scStreamUpServerSecs и keepalive приложения, например SSH |
| H3 настроен, но не используется | Убедитесь, что клиентский alpn содержит только "h3", и проверьте Browser Dialer |
| Низкая скорость многопоточного теста | Временно задайте "maxConcurrency": 1 |
| Слишком большие журналы обратного прокси | Настройте журналирование POST packet-up и padding в Referer |