Блог Clearway

Ошибка Xray: пошаговая диагностика, почему Xray не работает и как это чинить

Коротко

Если Xray не работает — читайте лог ядра, а не экран клиента: journalctl -u xray -f или xray run -c config.json вручную. По наблюдениям почти любая ошибка Xray-core сводится к одному из четырёх: (1) битый JSON — ядро не стартует (xray run -test -c config.json покажет строку); (2) рассинхрон path/Host/UUID/транспорта между инбаундом и клиентом — 400/404; (3) разные версии Xray-core на ноде и в приложении — unknown flow/unsupported; (4) чужой ответ unexpected status от CDN/nginx поверх исправного ядра. Отдельный случай — массовый внезапный обрыв у всех без ваших правок: это не ошибка ядра, а блокировка IP ноды. Ниже — как за пять минут понять, какая именно это ошибка Xray core, и точечно её снять.

Первый шаг: читаем лог ядра, а не клиента

Типовая ошибка диагностики — верить экрану клиента («нет соединения», «timeout») вместо лога Xray-core. Клиент показывает симптом; причину знает только ядро.

Включите подробный лог в config.json:

"log": {
 "loglevel": "warning",
 "access": "/var/log/xray/access.log",
 "error": "/var/log/xray/error.log"
}

loglevel: debug ставьте только на время отладки — он шумный и пишет чувствительные данные. Где смотреть на ноде:

  • systemd: journalctl -u xray -f (живой поток) или journalctl -u xray -e --no-pager (хвост);
  • ручной запуск: xray run -c /usr/local/etc/xray/config.json — ядро сразу печатает, на какой строке упало и почему;
  • Docker (Remnawave/Marzban): docker logs -f <container> или docker logs --tail 100 <container>.

Развилка, которая экономит время: если xray run -c не поднялся вообще — проблема в конфиге ядра (раздел ниже). Если ядро стартовало и слушает порт, а клиент ловит 400/404/unexpected status — проблема в стыковке инбаунда с клиентом или во фронте, а сам Xray исправен.

Конфиг не стартует: битый JSON и типовые ошибки структуры

Если xray run завершается мгновенно с Failed to read config:... или invalid character... after... — это ошибка Xray core на этапе парсинга, до сети ядро не дошло.

Проверка перед перезапуском:

xray run -test -c /usr/local/etc/xray/config.json # валидирует конфиг без запуска
jq. /usr/local/etc/xray/config.json > /dev/null # jq укажет строку с ошибкой JSON

Частые причины, почему Xray не работает на этапе конфига:

  • Хвостовая запятая после последнего элемента массива/объекта — строгий JSON её не допускает (в отличие от JSON5).
  • **Комментарии // или /* */** — Xray читает конфиг строгим JSON-ридером, комментарии ломают парсинг.
  • Порт занят: failed to listen... bind: address already in use. Найдите держателя порта: ss -tlnp | grep:443. Обычно это второй экземпляр Xray, nginx или не убитый старый контейнер.
  • Дубликат tag у инбаундов/аутбаундов — теги обязаны быть уникальны, иначе роутинг не соберётся.
  • no inbound/outbound found — пустой или неверно вложенный массив.

На проде правьте так: cp config.json config.json.bak → правка → xray run -test -c config.json → только при OK systemctl restart xray. Откат в одну строку: cp config.json.bak config.json && systemctl restart xray. Держите эту команму под рукой до перезапуска, а не после.

400/404 на инбаунде: рассинхрон path, Host и UUID

Ядро поднялось, туннель не идёт, в access.log или на стороне CDN — 400 Bad Request / 404 Not Found. Это почти всегда несовпадение параметров инбаунда и того, что реально шлёт клиент. Для VLESS поверх WS/XHTTP/gRPC совпадение критично до символа.

Сверьте построчно инбаунд на ноде и подписку в клиенте:

  • path/api/api//API. Слэш и регистр значимы. Для gRPC сверяйте serviceName.
  • Host / SNI — если перед нодой стоит nginx или CDN, он маршрутизирует по Host. Неверный host → 404 от фронта, ядро запрос вообще не увидит.
  • UUID (id) — лишний пробел при копировании или старый UUID после ротации ключей рвут соединение уже после рукопожатия.
  • network/securityws на ноде против xhttp в клиенте несовместимы; транспорт должен совпадать с обеих сторон.

Локализация «кто отдал 400/404»: постучитесь напрямую в порт ядра в обход фронта. Например, привяжите домен к локальному апстриму и проверьте:

curl -vk --resolve ваш-домен:443:127.0.0.1 https://ваш-домен/ваш-path

Если напрямую в порт ядра ответ идёт, а через публичный адрес — 404, виноват реверс-прокси или CDN. Для WebSocket в nginx обязательны proxy_set_header Upgrade $http_upgrade; и proxy_set_header Connection "upgrade"; — без них upgrade не проходит и приходит 400. Тонкости фронтинга Remnawave/панелей за CDN и специфику XHTTP (mode: packet-up, когда CDN отдаёт GET вместо POST) мы разбирали в отдельных материалах — здесь важно лишь: если параметры и заголовки совпали, а 404 остаётся, копайте во фронт, а не в config.json.

Несовпадение версий клиента и ноды: unsupported и молчаливые обрывы

Xray-core развивается быстро: XHTTP, обновления Reality, flow вроде xtls-rprx-vision появлялись в конкретных версиях. Если на ноде свежее ядро, а в клиенте старое (или наоборот) — получаете unknown flow, unsupported, rejected или просто обрыв без внятной строки в логе.

Сверьте версии:

xray version # на ноде
docker exec <ctr> xray version # в контейнере

В клиентах (v2rayN, v2rayNG, Happ, NekoBox, Streisand) версия встроенного ядра видна в «О программе»/настройках core. По наблюдениям у части клиентов ядро отстаёт от последнего релиза на несколько версий, и это нормально.

Типовые проявления рассинхрона:

  • unknown flow: xtls-rprx-vision — клиентское ядро старее ноды. Обновите приложение или временно уберите flow в подписке.
  • XHTTP не подключается у части клиентов — приложение собрано на ядре без поддержки xhttp. Лечится обновлением клиента, а не правкой ноды.
  • Reality не проходит — расхождение по shortId/publicKey либо клиент не умеет актуальную версию Reality.

Эксплуатационное правило: держите на ноде стабильную, не bleeding-edge версию, и зафиксируйте в поддержке минимально поддерживаемую версию клиента. Не обновляйте Xray-core на проде «вслепую» в пятницу — сначала прогоните тестовую подписку на 2–3 популярных клиентах и убедитесь, что подключение и flow живы.

unexpected status и TLS/Reality handshake

unexpected status 400/403/502 в логе клиента при живом ядре — это ответ промежуточного узла (CDN, балансировщик, nginx), а не Xray. Ядро ждёт свой протокол внутри TLS, а получает HTML-страницу ошибки или редирект.

Разбор по коду:

  • 403 — CDN/WAF отбил запрос (гео, rate-limit, не тот Host, режим «под атакой»). Проверьте, что домен реально проксируется и не включена защита с JS-челленджем.
  • 502/504 — фронт не достучался до апстрима: нода лежит, неверный upstream-порт или режет firewall. curl -I https://ваш-домен/path покажет, кто отвечает.
  • 400 — чаще всего не прошёл WebSocket-upgrade через прокси (см. раздел про 400/404).

TLS/Reality-специфика:

  • tls: handshake failure / certificate is not valid — истёк или не тот сертификат, SNI не совпадает с сертификатом. Проверка цепочки: openssl s_client -connect host:443 -servername ваш-sni </dev/null 2>/dev/null | openssl x509 -noout -dates.
  • REALITY: processed invalid connectiondest/serverNames в инбаунде не соответствуют реальному сайту-маскировке, либо publicKey в клиенте не от текущего privateKey.

Операторский момент, который часто принимают за ошибку ядра: когда unexpected status и обрывы приходят массово и внезапно у всех, а конфиг вы не трогали — это, как правило, блокировка самой ноды, а не Xray. При белых списках сервер в дата-центре блокируется первым; ядро исправно, порт слушает, но трафик до него не доходит. Правкой config.json это не лечится. Устойчивость даёт фронт через whitelisted-CDN: клиент ходит на «белый» адрес CDN, а тот проксирует на ноду, убирая из-под удара конкретный IP дата-центра. Отличить один случай от другого просто: если хоть у кого-то из другой сети всё работает, а падает по региону — это блок канала до ноды, а не ошибка Xray-core.

Чек-лист диагностики: от симптома к причине

Короткий алгоритм, чтобы не гадать, почему Xray не работает:

  1. xray run -test -c config.json — конфиг валиден? Нет → чините JSON/порт/дубликат тегов.
  2. Ядро стартовало? systemctl status xray и journalctl -u xray -f. Есть started/listening — идём дальше.
  3. curl -I https://домен/path снаружи — кто отвечает? 403/404/50x от фронта → рассинхрон path/Host или блок CDN.
  4. Прямой стук в порт ядра (в обход CDN, через --resolve) работает? Да → проблема в реверс-прокси/CDN, а не в Xray.
  5. Версии ядра ноды и клиента бьются по фичам (flow, XHTTP, Reality)? Нет → обновить клиент.
  6. Массовый внезапный обрыв у всех без ваших изменений и с рабочим ядром → почти наверняка блокировка IP ноды, а не ошибка ядра.

Перед любой правкой на проде: cp config.json config.json.bak, затем -test, и держите наготове строку отката. Диагностику всегда ведите от лога Xray-core — он единственный честный источник о том, что реально происходит внутри ядра; экран клиента лишь пересказывает симптом.

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

Почему Xray не работает сразу после старта, хотя конфиг не менял?

Сначала проверьте, жив ли процесс (systemctl status xray) и слушается ли порт (ss -tlnp | grep:ваш-порт). Затем — не истёк ли TLS-сертификат. Если ядро исправно и порт слушает, но трафик не доходит и упало разом у всех в одном регионе — это, как правило, блокировка IP ноды или сбой фронта/CDN, а не ошибка Xray-core. Ключевая проверка: работает ли подключение хоть у кого-то из другой сети.

Как понять, ошибка в конфиге Xray или в клиенте?

Запустите xray run -test -c config.json. Тест не проходит — проблема в конфиге ядра (JSON, занятый порт, дубликат тегов), клиент ни при чём. Тест проходит и ядро стартует, но клиент не подключается — причина в стыковке: path, Host, UUID, транспорт или версия клиентского ядра.

Что означает unexpected status 400/403/502 в логе Xray?

Это ответ не от ядра, а от промежуточного узла (CDN, nginx, балансировщик). 400 — обычно не прошёл WebSocket-upgrade; 403 — отбил CDN/WAF или не тот Host; 502/504 — фронт не достучался до ноды. Смотрите цепочку через curl -I https://домен/path, а не только конфиг ядра.

Может ли ошибка Xray быть из-за разных версий на сервере и в приложении?

Да, это частая причина. Новые транспорты (XHTTP, актуальный Reality, flow xtls-rprx-vision) требуют свежего Xray-core с обеих сторон. Старое ядро в клиенте даёт unknown flow/unsupported или молчаливый обрыв. Сверьте xray version на ноде и версию ядра в приложении, обновите клиент.

Xray-core не стартует с ошибкой address already in use — что делать?

Порт занят другим процессом. Найдите его: ss -tlnp | grep:443 (или ваш порт). Обычно это второй экземпляр Xray, nginx или не убитый старый контейнер. Освободите порт или смените его в инбаунде, затем xray run -test и перезапуск.

400/404 приходит только через CDN, а напрямую в ноду всё работает. Почему?

Ядро исправно, а реверс-прокси или CDN неправильно проксирует запрос: не пропускает WebSocket-upgrade, режет заголовки Host/Upgrade или не совпадает path/location. Для WS в nginx нужны заголовки Upgrade и Connection: upgrade. Проверьте прямой стук в порт ядра через curl --resolve — если он отвечает, копайте во фронт.

Как защититься от блокировки ноды, если сам Xray настроен верно?

Правкой config.json блокировку IP не лечат — ядро исправно, но трафик до него не доходит. Устойчивость даёт фронт через whitelisted-CDN: клиенты подключаются к «белому» адресу CDN, а он проксирует на ноду, скрывая IP дата-центра, который при белых списках блокируется первым.

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

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

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