Блог Clearway

Xray Reality vs XHTTP через CDN: что выбрать оператору

Коротко

Reality защищает протокол, XHTTP через белый CDN защищает IP-адрес — это разные слои, а не альтернативы. Если SYN до ноды доходит и рвётся уже установленная сессия — проблема в DPI, лечится Reality: бесплатно и без задержек. Если SYN не доходит вообще, а с зарубежного VPS всё работает — фильтр отработал по IP, до вашего хендшейка дело не дошло, и Reality здесь бессилен по построению. Тогда нужна смена точки входа на адрес из белого списка, то есть XHTTP через CDN на whitelist-домене. В проде правильный ответ на «reality или xhttp» — оба: два инбаунда на одной ноде и два хоста в подписке, Reality первым, CDN вторым.

Reality или XHTTP: разные уровни атаки

Запрос «xray reality xhttp» почти всегда приходит с неверной посылкой: что это два способа спрятать VPN и надо выбрать лучший. Они закрывают разные уровни.

Reality работает на уровне TLS-хендшейка. Нода подставляет сертификат чужого живого сайта (target/dest), и активный пробинг со стороны ТСПУ упирается в настоящий сайт: без валидного shortId и ключа Xray молча проксирует проверяющего на донора. По SNI, сертификату, JA3 и реакции на пробинг инбаунд неотличим от обычного HTTPS. Настройка донора, serverNames, flow: xtls-rprx-vision — тема отдельного материала про Reality, здесь не повторяем.

XHTTP (в старых сборках SplitHTTP) работает на уровне транспорта: укладывает VLESS-поток в обычные HTTP-запросы — даунлинк длинным стриминговым ответом, аплинк серией запросов. Сам по себе против DPI он даёт немного. Ценность появляется, когда перед ним стоит CDN: клиент подключается к edge-узлу, и вся видимая фильтру часть соединения — TLS до белого домена на белом IP.

Отсюда формулировка, которую стоит запомнить: Reality скрывает, что внутри соединения; CDN скрывает, куда идёт соединение. Блокировка по IP срабатывает до того, как дело дойдёт до содержимого, — значит, в этом сценарии Reality не помогает не из-за плохой настройки, а по построению. Общая картина «что вообще выживает под whitelist-режимом», включая Hysteria2, разбиралась отдельно; здесь — только выбор между двумя транспортами и гибрид.

Диагностика за пять минут: IP или протокол

Прежде чем менять что-то на проде, надо понять, что именно сломалось. Проверка делается с двух сторон одновременно: с клиента в зоне ограничений (телефон в режиме модема на мобильном операторе — самый показательный стенд) и с самой ноды.

С клиента:

ping -c 4 <IP_ноды>
nc -vz <IP_ноды> 443
curl -vk --connect-timeout 5 https://<IP_ноды>:443
mtr -rwc 20 <IP_ноды>

Одновременно на ноде:

sudo tcpdump -ni any 'tcp port 443 and tcp[tcpflags] & tcp-syn != 0'

Читаем результат:

Что видимДиагнозЧто делать
SYN до ноды не долетает, mtr обрывается на транзите оператораРежут по IP/подсетиReality не поможет. Нужен белый фронт или смена IP
SYN приходит, SYN-ACK уходит, у клиента таймаутФильтр на обратном пути, тот же IP-блокТо же самое
TLS-хендшейк проходит, сессия живёт 5–30 сек и рвётсяDPI по протоколу/поведениюReality, смена донора, flow: xtls-rprx-vision
Работает с зарубежного VPS, не работает с РФ-оператораФильтр на стороне РФ, нода исправнаСмотрим первые две строки
Не работает у одного оператора, работает у трёхЛокальный режим ТСПУБелый фронт для пострадавших

Частый пропускаемый маркер: IP отвечает на пинг, но 443 глухой — это не «сложный DPI», а фильтр по паре IP+порт. Перевесить Reality на 8443 иногда даёт ремиссию на несколько дней, но это отсрочка, а не решение.

Честная оговорка: по наблюдениям, при жёстких шатдаунах и whitelist-режимах на «сером» IP не выживает ни один транспорт — ни Reality, ни Shadowsocks-2022, ни голый WireGuard. Вопрос не в маскировке: пакет физически не выпускают за пределы разрешённого списка адресов.

Что реально меняется в конфиге при переходе на XHTTP за CDN

Полное руководство по XHTTP за CDN — в отдельной статье; здесь только дельта относительно привычного Reality-инбаунда, потому что именно на ней ломаются.

Путь пакета: клиент → TLS до cdn.example.ru (белый IP edge) → CDN проксирует на origin → nginx на ноде → Xray. TLS терминируется на edge, поэтому инбаунд слушает локально и без своего TLS:

{
 "tag": "vless-xhttp",
 "listen": "127.0.0.1",
 "port": 2087,
 "protocol": "vless",
 "settings": { "clients": [{ "id": "UUID" }], "decryption": "none" },
 "streamSettings": {
 "network": "xhttp",
 "security": "none",
 "xhttpSettings": {
 "host": "cdn.example.ru",
 "path": "/xh-braid",
 "mode": "packet-up"
 }
 }
}

Ссылка клиенту (одной строкой):

vless://[email protected]:443?encryption=none&security=tls&sni=cdn.example.ru&type=xhttp&host=cdn.example.ru&path=%2Fxh-braid&mode=packet-up#node-cdn

Четыре пункта, которые дают 90% инцидентов:

  1. mode задаётся явно, с обеих сторон. У Yandex CDN нет проксирования POST, поэтому аплинк уходит GET-запросами, а GET в качестве аплинка допустим только в режиме packet-up. Оставленный mode: auto даёт классику «в одном приложении работает, в другом нет»: клиент, выбравший stream-up, получает живой коннект и ноль трафика.
  2. security: none на локальном порту. Оставленный reality в xhttp-инбаунде несовместим с терминацией TLS на edge.
  3. extra идентичен на ноде и в клиенте — не «похож», а посимвольно совпадает. И чем он короче, тем лучше: экзотические obfuscation-поля переименовываются между версиями Xray-core, и конфиг под 25.x у клиента на 24.x просто не поднимется. Размещение uplink-данных (тело запроса против кастомного заголовка) разбиралось отдельно — это ровно тот случай, когда CDN режет заголовки и туннель рвётся.
  4. Буферизация на origin выключена. Без этого даунлинк-стрим копится в nginx и соединение выглядит как «висит и не отдаёт»:
location /xh-braid {
 proxy_pass http://127.0.0.1:2087;
 proxy_http_version 1.1;
 proxy_set_header Host $host;
 proxy_buffering off;
 proxy_request_buffering off;
 proxy_read_timeout 300s;
 proxy_send_timeout 300s;
}

На стороне CDN для этого path кэширование отключается обязательно: иначе edge закэширует первый ответ стрима и раздаст его всем следующим.

Сравнение и цена вопроса

ПараметрRealityXHTTP через белый CDN
От чего спасаетDPI по TLS, активный пробинг, SNI-фильтрБлокировка по IP, whitelist-режим ТСПУ
Спасает при блоке IPНетДа
Latency+0 к сети до ноды+20–60 мс на плечо через edge (по наблюдениям)
Стоимость трафика0, только канал VPS~0.6–3 ₽/ГБ в зависимости от CDN
OverheadМинимальный~5–15% на паддинг и HTTP-заголовки
CPU ноды и originНизкийВыше: поток мелких запросов, TLS дважды
ЭксплуатацияОдна нода, один конфигНода + origin-TLS + настройки CDN + учёт трафика

Про деньги стоит считать заранее, а не по факту счёта. XHTTP в режиме packet-up — это не «один поток», а поток запросов: на гигабайт трафика их приходится на порядки больше, чем у обычной статики, ради которой CDN и строились. У CDN, где запросы тарифицируются отдельно, это заметная часть чека, и она же объясняет рост CPU на origin.

Порядок величин на живой ноде: 600 активных подписок, по наблюдениям 20–40 ГБ в месяц на активного пользователя — это 12–24 ТБ. Полный перевод такого объёма на CDN по 1.5 ₽/ГБ — это 18–36 тысяч рублей в месяц, включая ютуб ваших пользователей. Отсюда здоровая схема не «переехать на CDN», а держать CDN как второй маршрут для тех регионов и операторов, где прямой IP уже не проходит.

И ограничение, которое экономит часы экспериментов: Reality за CDN не существует. CDN терминирует TLS у себя, Reality требует сквозного TLS до вашей ноды — вещи взаимоисключающие. Любая инструкция вида «поставьте Reality за Cloudflare» технически невозможна.

Что выбрать: четыре сценария и гибридная схема

Практический выбор «что выбрать reality xhttp» сводится к четырём ситуациям.

  1. IP живой, жалобы единичные, деградация по вечерам. Reality, flow: xtls-rprx-vision, порт 443. XHTTP тут добавит только задержек и счетов.
  2. Часть операторов отвалилась, часть работает. Локальный фильтр. Reality остаётся основным, XHTTP-через-CDN добавляется вторым хостом в подписку для пострадавших.
  3. Нода мертва у всех РФ-операторов, из-за рубежа работает. IP в блоке. Только CDN-фронт. Переезд на новый «серый» IP по наблюдениям даёт недели, а не месяцы.
  4. Регион в whitelist-режиме. Только CDN на подтверждённо белом домене, причём проверять надо каждую /24: у одного хостера часть подсетей в списке, часть нет, и состав меняется.

Гибрид собирают заранее, а не в момент шатдауна. Два инбаунда на одной ноде:

"inbounds": [
 { "tag": "vless-reality", "port": 443, /*... reality... */ },
 { "tag": "vless-xhttp", "listen": "127.0.0.1", "port": 2087 /*... xhttp... */ }
]

В подписке отдаются оба хоста с говорящими именами (RU-fast, RU-backup-cdn), Reality первым: пользователь переключится сам, когда основной перестанет ходить. В Remnawave, Marzban и 3x-ui это два хоста на профиль — механика описана в материалах про панели за CDN.

Проверка перед раздачей клиентам:

ss -lntp | grep -E '443|2087'
journalctl -u xray -n 50 --no-pager | grep -iE 'failed|rejected'
curl -s -o /dev/null -w '%{http_code} %{time_total}\n' https://cdn.example.ru/xh-braid

Последняя команда на живом XHTTP-пути не должна отдавать быстрый 404: мгновенный 404 означает, что CDN не пробрасывает этот path на origin, а нормальная реакция — соединение, которое держится до таймаута.

Частые ошибки при переезде с Reality на XHTTP

Из типовых разборов «всё сделал по гайду, но не работает»:

  • mode: auto вместо явного packet-up. Работает у части клиентов, у остальных коннект без трафика.
  • extra разошёлся между нодой и клиентом. Хендшейк проходит, данные не идут или рвутся на больших ответах.
  • CDN кэширует path. Первый пользователь работает, остальные получают пустое тело или чужой ответ.
  • Origin отдаёт HTTP там, где CDN требует валидный TLS до источника. Проверка цепочки: openssl s_client -connect <IP_ноды>:8443 -servername cdn.example.ru </dev/null | head -20.
  • Разные версии Xray-core на ноде и в приложении. XHTTP меняется от версии к версии; при разборе инцидента первым делом сверяйте xray version и версию ядра в клиенте.
  • Подписка закэширована на клиенте. Особенно на iOS: конфиг обновили, приложение показывает старый. Отдавайте подписку с Cache-Control: no-store, в тяжёлых случаях — переподключение подписки заново.

И обязательное для прода: копия конфига до правки инбаундов и готовый откат.

cp /usr/local/etc/xray/config.json /root/xray-config.$(date +%F-%H%M).json
xray -test -config /usr/local/etc/xray/config.json && systemctl restart xray
# откат:
# cp /root/xray-config.<метка>.json /usr/local/etc/xray/config.json && systemctl restart xray

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

Можно ли поставить Reality за CDN и получить оба свойства сразу?

Нет. CDN терминирует TLS на своём edge-узле, а Reality построен на том, что TLS-хендшейк идёт сквозным до вашей ноды и подменяет сертификат чужого сайта. Как только TLS разрывается на промежуточном узле, подменять уже нечего. Технически корректный способ получить оба свойства — два независимых инбаунда: Reality напрямую и XHTTP через CDN, оба хоста в подписке.

Reality в 2026 году ещё работает или уже всё?

Работает как защита от DPI-классификации и активного пробинга. Не работает как защита от блокировки по IP — и никогда не работала, это разные уровни. Массовое «Reality умер» в отзывах почти всегда означает, что у оператора выбило IP ноды. Отличить одно от другого можно за пять минут: tcpdump на ноде по SYN на 443 — если SYN от клиента не приходит, дело точно не в Reality.

Почему XHTTP через CDN работает в одном приложении и не работает в другом?

Три причины по частоте. Первая — mode: auto: у Yandex CDN нет POST, аплинк уходит GET-запросами, а GET допустим только при mode: packet-up; клиент, выбравший stream-up, получает мёртвое соединение. Вторая — расхождение extra между нодой и клиентом. Третья — разные версии Xray-core: поля XHTTP переименовывались, и экзотические obfuscation-параметры ломают совместимость. Разбор начинайте с явного packet-up с обеих сторон.

Какой mode выбирать: packet-up, stream-up или stream-one?

За CDN без поддержки POST — только packet-up, вариантов нет. stream-up (аплинк одним длинным запросом) даёт меньше накладных расходов и подходит для прямого подключения или CDN с полноценным POST-проксированием. stream-one объединяет оба направления в одно соединение, требует поддержки с обеих сторон и на многих CDN не проходит. Для продакшена за whitelist-CDN ставьте packet-up явно и не полагайтесь на auto.

Есть ли смысл в XHTTP без CDN?

Ограниченный. Голый XHTTP на вашем же IP даёт трафик, похожий на обычный HTTP-стриминг, и по наблюдениям иногда лучше переживает мягкий DPI, чем WebSocket. Но главную задачу — увести точку входа на белый адрес — без CDN он не решает: пакет по-прежнему адресован в заблокированную подсеть. Если ноду режут по IP, переход на XHTTP без смены фронта не изменит ничего.

Как не разориться на трафике через CDN?

Не переводить на CDN весь трафик. Reality остаётся основным маршрутом и обслуживает большую часть гигабайтов, CDN подключается вторым хостом только для регионов и операторов, где прямой IP не проходит. Дополнительно: смотрите не только цену за гигабайт (разброс между провайдерами кратный, примерно от 0.6 до 3 ₽/ГБ), но и тарификацию запросов — packet-up генерирует на гигабайт несопоставимо больше запросов, чем обычная статика, и у части CDN это отдельная строка в счёте.

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

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

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