Два инбаунда: зачем VLESS Reality и XHTTP одновременно
Транспорт в 3x-ui выбирают не по вкусу, а по тому, что именно у вас ломается. ТСПУ режет по протоколу и SNI — спасает Reality. Нода уже отфильтрована по IP или абонент оказался в whitelist-режиме — Reality бесполезен: пакеты до вашего адреса просто не доходят.
| Инбаунд | Что видит фильтр | Где ломается |
|---|---|---|
raw (tcp) + reality, 443 | TLS 1.3 handshake к чужому домену (dest) | блокировка IP ноды; блок самого dest |
xhttp + security: none за CDN | HTTPS к домену CDN-провайдера | несовпадение mode/extra, кастомные заголовки |
ws/grpc за CDN | WebSocket-upgrade / HTTP-2-стрим | часть российских CDN не проксирует апгрейд и капризна к таймаутам |
Практика: Reality забирает ~90% трафика, XHTTP-инбаунд стоит вторым плечом и включается, когда первый уже мёртв. Оба живут в одной панели и уезжают абоненту одной подпиской — клиент сам переберёт точки входа. Ради второго плеча и существует whitelist-CDN как сервис (Clearway): нода остаётся вашей, а вход приходит с адреса, который у оператора не под фильтром.
Общую теорию Reality, обфускацию XHTTP и выбор whitelisted-подсетей разбирали отдельно — здесь только то, что нажимается и вводится в 3x-ui.
Перед настройкой: версия Xray, файлы панели, бэкап и откат
Сначала зафиксируйте, с чем работаете: форма инбаунда заметно менялась от ветки к ветке. XHTTP появился в Xray 24.11.x (пришёл на смену splithttp), а в ветке 25.x транспорт tcp переименован в raw — старое имя пока принимается, но в свежих сборках 3x-ui в списке вы увидите именно raw.
x-ui # интерактивное меню панели
x-ui settings # логин, порт панели, webBasePath
systemctl status x-ui
/usr/local/x-ui/bin/xray-linux-amd64 -version
| Путь | Что это |
|---|---|
/etc/x-ui/x-ui.db | вся панель: инбаунды, клиенты, подписки, настройки. Единственное, что нужно бэкапить |
/usr/local/x-ui/bin/config.json | сгенерированный конфиг Xray. Руками не править — панель перезапишет при следующем сохранении |
/usr/local/x-ui/bin/xray-linux-amd64 | бинарь ядра, им же генерируются ключи |
Бэкап перед любой правкой на проде:
systemctl stop x-ui
cp /etc/x-ui/x-ui.db /root/x-ui.db.$(date +%F-%H%M).bak
systemctl start x-ui
Откат — та же операция в обратную сторону: остановить сервис, вернуть .bak на место /etc/x-ui/x-ui.db, запустить. Этим же способом инбаунды переезжают на другой сервер: ставите там ту же ветку 3x-ui и подкладываете БД. UUID, ключи Reality и Subscription ID переживают переезд, а вот адрес в клиентских ссылках — нет, абонентам придётся обновить подписку.
Два момента, которые экономят час отладки. Первый: порт панели и порт инбаунда не должны пересекаться — на занятом порту 3x-ui не всегда внятно ругается, инбаунд просто молча не поднимается. Второй: у формы инбаунда есть переключатель в JSON (иконка </>) — часть полей XHTTP (extra, размещение uplink и служебных ключей) в GUI отсутствует в принципе, и задаются они только там.
Настройка 3x-ui VLESS Reality: поля формы и ключи x25519
Inbounds → Add Inbound. Разбор полей схемы 3x-ui VLESS Reality в том порядке, как они идут в форме.
| Поле | Значение | Комментарий |
|---|---|---|
| Remark | reality-443 | попадает в имя конфига у абонента |
| Protocol | vless | — |
| Listening IP | пусто | пусто = 0.0.0.0 |
| Port | 443 | почти всегда 443: на нестандартном порту маскировка под обычный сайт теряет смысл |
| Client → ID | UUID (кнопка генерации) | «ключ» абонента |
| Client → Email | уникальная метка | уникальна в пределах всей панели, а не инбаунда; дубль ломает учёт трафика |
| Client → Flow | xtls-rprx-vision | только для raw/tcp. У XHTTP/WS/gRPC поле обязано быть пустым |
| Decryption | none | в свежих ветках Xray у VLESS появилось собственное шифрование (encryption/decryption). Включать только когда все клиенты на такой же свежей версии |
| Transmission | raw (старые сборки — tcp) | — |
| Security | reality | — |
| uTLS (fingerprint) | chrome | должен совпасть с fp в ссылке |
| Dest / SNI | чужой домен:443 | см. ниже |
| Private / Public key | кнопка Get New Cert или вручную | приватный остаётся на ноде, публичный уходит в ссылку как pbk |
| Short IDs | 8–16 hex, чётное число символов | можно несколько, клиент присылает один |
| SpiderX | / | в ссылке — spx=%2F |
Если кнопка генерации не отработала (бывает на кастомных сборках) — считаем руками:
/usr/local/x-ui/bin/xray-linux-amd64 x25519
openssl rand -hex 8 # 16 hex-символов, годится как shortId
Нюанс свежих Xray: в части сборок вывод x25519 подписан не Private key / Public key, а PrivateKey / Password. Password здесь — это публичный ключ, тот самый pbk для клиента. Перепутать местами — классическая причина «подключается, но трафика нет».
Про dest: критерии выбора (TLS 1.3, HTTP/2, валидный сертификат, домен не под фильтром в РФ и не с вашего же хостера) разбирали в статье про Reality. От себя добавим честную оговорку: www.cloudflare.com как дефолт из мануалов — плохой выбор именно из-за тиражности, берите менее очевидный крупный домен.
После сохранения проверьте снаружи, что нода отдаёт чужой сертификат, а не ваш:
openssl s_client -connect NODE_IP:443 -servername www.samsung.com \
</dev/null 2>/dev/null | openssl x509 -noout -issuer -subject
Если в ответе ваш Let's Encrypt — Reality не активен, и вы только что показали фильтру свой домен.
Второй инбаунд: VLESS XHTTP за CDN, mode packet-up и extra
TLS терминируется на edge, до ноды идёт уже расшифрованный HTTP — значит на инбаунде TLS не нужен: Transmission: xhttp, Security: none, Listening IP 127.0.0.1 (если фронт на том же хосте) либо 0.0.0.0 с фаерволом, пускающим только адреса вашего gateway.
| Поле | Значение |
|---|---|
| Port | локальный, например 25454 |
| Transmission | xhttp |
| Security | none |
| Path | /api/v1/sync/ — длинный, неугадываемый, ровно тот же на CDN |
| Host | домен CDN-edge, который увидит абонент |
| Mode | packet-up |
| Client → Flow | пусто |
Про mode — техфакт, на котором ломается большинство операторов. У Yandex CDN нет POST, поэтому XHTTP-аплинк уходит GET-запросами, а GET допустим только при mode: packet-up. Оставите auto — клиент сам выберет режим с POST, получит от edge отказ и повиснет на пустом соединении. Симптом узнаваемый: «в Happ работает, в v2rayN нет» — или наоборот, в зависимости от того, что клиент выбрал за вас.
Второе правило: extra на ноде и extra в клиенте совпадают полем в поле, не «примерно». Служебные данные раскладывайте по тем каналам, которые edge реально доносит до origin — тело запроса, query, cookie; произвольные кастомные заголовки CDN режет, и туннель рвётся с unexpected EOF (это разбиралось в отдельной статье про 3x-ui за CDN). И чем экзотичнее поля обфускации, тем выше шанс, что клиент на другой версии Xray их не поймёт: совместимость между ветками тут никем не гарантирована. Начинайте с минимума, добавляйте по факту подтверждённой проблемы.
Эти поля живут в JSON-редакторе инбаунда, в GUI их нет. Итог смотрите в сгенерированном конфиге:
{
"network": "xhttp",
"security": "none",
"xhttpSettings": {
"path": "/api/v1/sync/",
"host": "edge.example.com",
"mode": "packet-up",
"extra": { "xPaddingBytes": "100-1000" }
}
}
Проверка цепочки:
curl -s -o /dev/null -w '%{http_code}\n' https://edge.example.com/api/v1/sync/ # через фронт
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:25454/api/v1/sync/ # мимо CDN
Важно различать «404 от edge» (путь не проброшен на CDN) и «висит без ответа» (путь дошёл, но не совпал mode или extra). И закрывайте origin — фаерволом по адресам gateway или секрет-заголовком от edge: открытый резервный инбаунд находят сканом за сутки.
Отдельно запомните: 3x-ui не разделяет «инбаунд на сервере» и «адрес, который получает клиент». Автоссылка и QR из панели для этого инбаунда ведут на IP ноды напрямую, мимо CDN, и работать не будут.
Клиентская ссылка, подписка и Subscription ID
Ссылку полезно уметь читать глазами — большинство «не подключается» видно прямо в параметрах.
Reality (панель отдаёт корректно, кнопкой QR/копирования):
vless://[email protected]:443?type=tcp&security=reality&fp=chrome
&sni=www.samsung.com&pbk=PUBLIC_KEY&sid=1a2b3c4d&spx=%2F
&flow=xtls-rprx-vision#node1-reality
XHTTP за CDN (собирается вручную, один раз на инбаунд — дальше меняется только UUID):
vless://[email protected]:443?type=xhttp&security=tls
&sni=edge.example.com&host=edge.example.com&path=%2Fapi%2Fv1%2Fsync%2F
&mode=packet-up&extra=%7B%22xPaddingBytes%22%3A%22100-1000%22%7D#node1-cdn
Чек-лист сверки: pbk — публичный ключ с ноды, sid — один из shortId, sni/fp — как в форме, flow присутствует только у Reality-ссылки, mode и extra у XHTTP — байт в байт как на ноде. extra едет URL-энкоднутым JSON; если клиент при импорте молча его теряет — это ограничение клиента, а не ваша ошибка: заводите конфиг вручную или отдавайте JSON-подписку.
Подписка: Panel Settings → Subscription, порт по умолчанию 2096, путь /sub/, JSON-путь /json/. У каждого клиента должен быть заполнен Subscription ID — по нему 3x-ui группирует конфиги, и абонент получает Reality и XHTTP одной ссылкой. Домен подписки держите отдельным именем от нод: подписка отваливается первой, когда фильтр начинает работать по доменам. Там же в клиенте живут totalGB и срок действия — штатные лимиты панели, они считаются по Email-метке, поэтому дубли метки ломают и учёт, и автоотключение.
Про кэш — по наблюдениям: часть мобильных клиентов (особенно iOS) держит старую версию подписки десятками минут и не подхватывает правку инбаунда. Сменили mode, path или ключи — просите абонента обновить подписку вручную или переподписаться; на стороне фронта помогает отдача подписки с Cache-Control: no-store.
Диагностика: типовые ошибки vless 3x ui
Логи в первую очередь — с консоли быстрее, чем со страницы панели:
journalctl -u x-ui -f --no-pager
ss -tlnp | grep xray
Уровень лога Xray переключается в Xray Configuration → Log: на время отладки info, после — обратно, иначе диск съедается на живом трафике.
| Симптом | Причина | Что делать | ||
|---|---|---|---|---|
failed to find user / invalid user | UUID в ссылке не совпал с клиентом инбаунда | пересоздать ссылку из панели, не править UUID руками | ||
| Клиент «connected», трафика ноль (Reality) | перепутаны private/public, либо разъехались sid/sni/fp | сверить с формой; помнить, что Password в выводе x25519 — публичный ключ | ||
REALITY: processed invalid connection изредка | чужие сканеры и пробы | нормально, реагировать не нужно | ||
| Рвётся на handshake, dest недоступен с ноды | dest не отвечает TLS 1.3 или сам заблокирован | сменить dest, проверить openssl s_client с ноды | ||
Клиент с flow не подключается к XHTTP/WS | xtls-rprx-vision живёт только с raw/tcp | очистить поле Flow у клиента этого инбаунда | ||
| XHTTP: edge отдаёт 404 | путь не проброшен на CDN или не совпал с path | выровнять path на фронте и в инбаунде | ||
| XHTTP: соединение висит, данных нет | mode разъехался (auto против packet-up) | поставить packet-up с обеих сторон | ||
XHTTP: unexpected EOF | служебные данные ушли в кастомный заголовок, edge его срезал | переложить uplink и служебные ключи в тело/query/cookie | ||
| Работает в одном приложении, не работает в другом | клиент не понял extra или подставил свой mode | убрать редкие поля обфускации, оставить минимум | ||
| Инбаунд «есть», порт не слушается | порт занят панелью или другим инбаундом | `ss -tlnp \ | grep -E ':443\ | :25454'`, сменить порт |
| Правка не применилась | ядро не перезапустилось | x-ui restart, затем сверить /usr/local/x-ui/bin/config.json |
Последнее правило универсальное: в споре формы и config.json прав config.json — в нём лежит то, что работает прямо сейчас, а не то, что вы видите на экране.