Remnawave: установка панели и нод

Установка панели и нод Remnawave: панель в Docker за reverse proxy с TLS, ноды с VLESS Reality Vision, подписки, бэкапы, обновления и разбор типовых поломок.

Актуально на сентябрь 2026 года: панель Remnawave 3.4.4, нода 3.4.1 со встроенным Xray-core 26.7.28, страница подписки 8.0.0. Азы Docker и Xray пропускаем: упор на схему из нескольких нод и на то, что ломается в эксплуатации.

Что понадобится

  • VPS под панель: минимум 2 vCPU и 2 ГБ RAM, лучше 4 и 4, диск 20 ГБ, Ubuntu или Debian.
  • VPS на каждую локацию: ноде хватает 1 vCPU и 1 ГБ, но под нагрузкой Xray прожорлив, так что берите с запасом. У ForestsNet Cloud это серверы в Нидерландах (Эйгелсховен) и Германии (Франкфурт) на AMD EPYC 7713 с NVMe и портом 1–10 Гбит/с; IP-адреса ежедневно проверяются на блокировки ТСПУ.
  • Домен: A-записи panel.example.com и sub.example.com — на сервер панели, nl1.example.com и de1.example.com — на ноды, см. Как привязать домен к серверу.
  • Docker с плагином Compose на всех серверах: curl -fsSL https://get.docker.com | sh, подробнее — Docker и Docker Compose.
  • Базовая защита: усиление SSH, файрвол ufw, чеклист базовой защиты VPS.

Шаг 1. Разложите компоненты по серверам

Почти любая поломка Remnawave — это сервис, не достучавшийся до соседа, поэтому начнём со схемы.

  • Панель (remnawave/backend): API и веб-интерфейс на порту 3000, метрики Prometheus на 3001. Рядом PostgreSQL 18 со всеми данными и Valkey — совместимые с Redis очереди и кэш на unix-сокете. Всё опубликовано только на 127.0.0.1, наружу смотрит reverse proxy.
  • Нода (remnawave/node): контейнер с Xray-core. Конфигурацию, пользователей и ключи ей присылает панель.
  • Страница подписки (remnawave/subscription-page): порт 3010. Отдаёт подписки со своего домена, а в API панели ходит по токену.

Соединения:

  • Панель → нода: панель сама подключается к ноде на NODE_PORT (по умолчанию 2222) по HTTPS со взаимной проверкой сертификатов, только TLS 1.3. Удостоверяющий центр — панель, сертификат и ключ ноды упакованы в SECRET_KEY. Сама нода к панели не ходит.
  • Клиент → нода: 443/TCP, инбаунд VLESS Reality.
  • Клиент → страница подписки: 443 на sub.example.com, дальше страница идёт в панель по внутренней docker-сети.

Если панель упала, ноды продолжают обслуживать заведённых пользователей с последней конфигурацией, но новые пользователи, продления и отключения не применятся до её возвращения. Держите панель на отдельном VPS, а не на ноде: адреса нод чаще приходится менять.

Шаг 2. Установите панель

mkdir -p /opt/remnawave && cd /opt/remnawave
curl -o docker-compose.yml https://raw.githubusercontent.com/remnawave/backend/refs/heads/main/docker-compose-prod.yml
curl -o .env https://raw.githubusercontent.com/remnawave/backend/refs/heads/main/.env.sample

В compose-файле образ remnawave/backend:3 (обновления в пределах третьей версии), PostgreSQL 18.4 и Valkey 9. Сгенерируйте секреты и пароль базы:

sed -i "s/^APP_SECRET=.*/APP_SECRET=$(openssl rand -hex 64)/" .env
sed -i "s/^METRICS_PASS=.*/METRICS_PASS=$(openssl rand -hex 64)/" .env
sed -i "s/^WEBHOOK_SECRET_HEADER=.*/WEBHOOK_SECRET_HEADER=$(openssl rand -hex 64)/" .env
pw=$(openssl rand -hex 24)
sed -i "s/^POSTGRES_PASSWORD=.*/POSTGRES_PASSWORD=$pw/" .env
sed -i "s|^\(DATABASE_URL=\"postgresql://postgres:\)[^@]*\(@.*\)|\1$pw\2|" .env

JWT-секретов из старых инструкций больше нет: в 3.x сессии и API-токены подписывает один APP_SECRET, и с change_me панель не стартует. При обновлении со второй версии переименуйте JWT_AUTH_SECRET в APP_SECRET, сохранив значение.

Пропишите домены в .env:

FRONT_END_DOMAIN=panel.example.com
PANEL_DOMAIN=panel.example.com
SUB_PUBLIC_DOMAIN=sub.example.com

FRONT_END_DOMAIN задаёт CORS, PANEL_DOMAIN — ссылки в уведомлениях, из SUB_PUBLIC_DOMAIN панель собирает ссылки на подписку: без https:// и слеша в конце. Без отдельной страницы подписки сюда пишут panel.example.com/api/sub.

docker compose up -d && docker compose ps
Важно: пароль PostgreSQL применяется только при первом создании тома remnawave-db-data: поменяете его в .env позже — панель не подключится к базе и уйдёт в перезапуски. Правки .env подхватываются только при пересоздании контейнеров — docker compose down && docker compose up -d, restart не поможет.

Шаг 3. Поставьте reverse proxy и закройте панель

Бэкенд принимает только запросы через прокси по HTTPS: без заголовков X-Forwarded-For и X-Forwarded-Proto: https он молча рвёт соединение и пишет в лог «Reverse proxy and HTTPS are required». Работать на подпути вроде /panel панель не умеет — только в корне домена или поддомена.

Возьмём Caddy: он сам получит сертификаты Let's Encrypt и выставит заголовки, основы — в статье Caddy: автоматический HTTPS. Панель сразу закроем «стуком»: без секретной cookie Caddy обрывает соединение, ничего не ответив. Форма регистрации не торчит в интернет, а это важно: первый зарегистрированный пользователь становится суперадмином.

mkdir -p /opt/remnawave/caddy && cd /opt/remnawave/caddy
echo "RW_KNOCK=$(openssl rand -hex 16)" > .env

Caddyfile:

panel.example.com {
	@knock query rw={$RW_KNOCK}
	@allowed header Cookie *rw_gate={$RW_KNOCK}*
	route {
		header @knock Set-Cookie "rw_gate={$RW_KNOCK}; Path=/; Max-Age=2592000; HttpOnly; Secure; SameSite=Lax"
		reverse_proxy @knock remnawave:3000
		reverse_proxy @allowed remnawave:3000
		abort
	}
}

sub.example.com {
	reverse_proxy remnawave-subscription-page:3010
}

docker-compose.yml:

services:
  caddy:
    image: caddy:2.11
    container_name: caddy
    restart: always
    env_file: .env
    ports:
      - "80:80"
      - "443:443"
    networks:
      - remnawave-network
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - caddy-data:/data

networks:
  remnawave-network:
    external: true

volumes:
  caddy-data:

Запустите docker compose up -d. Вход — по ссылке https://panel.example.com/?rw=<значение RW_KNOCK>: Caddy поставит cookie на 30 дней, дальше панель открывается по обычному адресу. Сеть remnawave-network создал compose панели, поэтому Caddy видит контейнеры по именам.

Совет: на сервере панели откройте в ufw только 22, 80 и 443. Порты, опубликованные Docker, обходят ufw, поэтому 3000, 3001 и 6767 в compose панели привязаны к 127.0.0.1 — не меняйте это на 0.0.0.0.

Встроенной cookie-защиты в панели 3.x нет. Документация предлагает remnawave/caddy-with-auth с TOTP (образ не обновлялся с марта 2025 года), TinyAuth перед nginx и Cloudflare Zero Trust; в nginx «стук» делается через map $http_cookie, как в скрипте eGames. Вход через Telegram OAuth с такими защитами может не работать.

Шаг 4. Первый вход и страница подписки

Откройте ссылку со «стуком» и зарегистрируйтесь — этот аккаунт станет суперадмином. В Remnawave Settings подключите passkey и отключите вход по паролю; запертый доступ возвращает Rescue CLI: docker exec -it remnawave cli.

Для страницы подписки выпустите API-токен в Remnawave Settings → API Tokens. В 3.x у токенов обязательный срок действия — ставьте его с запасом и заведите напоминание: просроченный токен роняет выдачу подписок всем сразу.

/opt/remnawave/subscription/docker-compose.yml:

services:
  remnawave-subscription-page:
    image: remnawave/subscription-page:8.0.0
    container_name: remnawave-subscription-page
    hostname: remnawave-subscription-page
    restart: always
    env_file: .env
    ports:
      - "127.0.0.1:3010:3010"
    networks:
      - remnawave-network

networks:
  remnawave-network:
    external: true

.env рядом:

APP_PORT=3010
REMNAWAVE_PANEL_URL=http://remnawave:3000
REMNAWAVE_API_TOKEN=токен_из_панели
TRUST_PROXY=1

Схема в REMNAWAVE_PANEL_URL — именно http://. В .env.sample репозитория стоит https://remnawave:3000, но внутри docker-сети бэкенд говорит по HTTP, а заголовки для проверки прокси страница добавляет только к адресу с http://. TRUST_PROXY=1 — один доверенный прокси, Caddy. Запуск — docker compose up -d.

Ссылка на подписку выглядит как https://sub.example.com/<shortUuid>: браузер получает страницу с инструкциями, приложение — конфигурацию.

Шаг 5. Соберите профиль VLESS Reality Vision

Профиль конфигурации (Config Profiles) — полный конфиг Xray для ноды. Панель проверяет его облегчённым ядром при каждом сохранении и сама добавляет пользователей в инбаунды, поэтому clients оставляйте пустым. Для VLESS с Reality поверх RAW (бывший TCP) клиенты автоматически получают flow: xtls-rprx-vision.

Цель Reality: свой сайт на ноде

Reality заимствует TLS-рукопожатие у сайта-цели (target, старое имя dest) и туда же пересылает всё, что не прошло аутентификацию. Схема — selfsteal: у каждой ноды свой поддомен с A-записью на её IP, на ноде Caddy с сертификатом Let's Encrypt слушает 127.0.0.1:9443, в target — этот адрес, в serverNames — ваши домены. SNI честно резолвится в адрес ноды, посетитель без ключа видит обычный сайт, а TLS 1.3 и HTTP/2, которых Reality требует от цели, Caddy отдаёт по умолчанию. Один профиль обслуживает все ноды, а сгоревший поддомен меняется в панели: клиенты получат новый SNI с обновлением подписки.

На серверах ForestsNet Cloud другой схемы и нет: Условия использования запрещают выдавать сервер за чужой сайт, то есть указывать чужой домен в SNI, target или сертификате. Такие настройки приводят к блокировкам адресов, а это бьёт по всем клиентам сети.

В редакторе профиля есть и готовый шаблон selfsteal из репозитория remnawave/templates (меню → Скачать с Github): там nginx на unix-сокете /dev/shm/nginx.sock с PROXY protocol (xver: 1). Вариант с Caddy на 127.0.0.1:9443 ниже проще: без сокета и без монтирования /dev/shm в контейнер ноды. Готовый установщик selfsteal на Caddy или nginx есть в скриптах DigneZzZ из каталога сообщества на docs.rw.

Ключи, shortIds и minClientVer

Ключи x25519 генерируются в редакторе профиля: меню внизу → Сгенерировать ключи. В профиль идёт privateKey, публичный ключ панель отдаёт клиентам сама. shortIds — hex-строки чётной длины до 16 символов, например из openssl rand -hex 8; клиенты получают первый элемент массива, поэтому первым ставьте непустой ID.

Xray в ноде 3.4.1 по умолчанию пускает по Reality только клиентов на Xray 26.3.27 и новее. Mihomo и sing-box, а на них работают Clash Verge, FlClashX, Hiddify и Karing, представляются версией 1.8 и получают отказ, поэтому шаблоны Remnawave ставят "minClientVer": "0.0.0". XTLS предупреждает: старые и сторонние клиенты выделяются TLS-отпечатком, и снятие порога повышает риск блокировки. Если все ваши пользователи на свежих Xray-клиентах — Happ, v2rayNG, v2rayN, — строку можно убрать.

Профиль для selfsteal, общий для всех нод:

{
  "log": { "loglevel": "warning" },
  "inbounds": [
    {
      "tag": "VLESS_REALITY",
      "listen": "0.0.0.0",
      "port": 443,
      "protocol": "vless",
      "settings": { "clients": [], "decryption": "none" },
      "sniffing": { "enabled": true, "destOverride": ["http", "tls", "quic"] },
      "streamSettings": {
        "network": "raw",
        "security": "reality",
        "realitySettings": {
          "target": "127.0.0.1:9443",
          "xver": 0,
          "serverNames": ["nl1.example.com", "de1.example.com"],
          "privateKey": "СГЕНЕРИРУЙТЕ_В_ПАНЕЛИ",
          "shortIds": ["9f3c1a7e5b2d4086"],
          "minClientVer": "0.0.0"
        }
      }
    }
  ],
  "outbounds": [
    { "protocol": "freedom", "tag": "DIRECT" },
    { "protocol": "blackhole", "tag": "BLOCK" }
  ],
  "routing": {
    "rules": [
      { "ip": ["geoip:private"], "outboundTag": "BLOCK" },
      { "domain": ["geosite:private"], "outboundTag": "BLOCK" },
      { "protocol": ["bittorrent"], "outboundTag": "BLOCK" }
    ]
  }
}

Правила private не пускают клиентов в локальные сервисы ноды, включая Caddy на 127.0.0.1. Правило bittorrent ловит лишь часть торрентов — об остальном в статьях Как заблокировать торренты на своём VPN или прокси и Детектор торрентов для Remnawave.

Шаг 6. Установите ноды

Сайт для selfsteal

На каждой ноде поднимите Caddy: он получит сертификат Let's Encrypt на поддомен ноды по HTTP-01 через порт 80 и будет отдавать сайт на 127.0.0.1:9443. DNS-запись ноды не проксируйте через Cloudflare — клиенты должны приходить прямо на IP. Положите в /opt/selfsteal/site правдоподобную статику, хотя бы index.html.

/opt/selfsteal/Caddyfile:

{
	https_port 9443
	default_bind 127.0.0.1
	auto_https disable_redirects
}

http://nl1.example.com {
	bind 0.0.0.0
	redir https://nl1.example.com{uri} permanent
}

https://nl1.example.com {
	root * /srv/site
	file_server
}

/opt/selfsteal/docker-compose.yml:

services:
  caddy:
    image: caddy:2.11
    container_name: selfsteal
    restart: always
    network_mode: host
    volumes:
      - ./Caddyfile:/etc/caddy/Caddyfile:ro
      - ./site:/srv/site:ro
      - caddy-data:/data

volumes:
  caddy-data:

Нода

В панели откройте Nodes → Management → +: страна, имя, адрес и порт 2222. Кнопка Copy docker-compose.yml скопирует файл с SECRET_KEY — это base64 от JSON с сертификатом удостоверяющего центра панели, сертификатом и ключом ноды и публичным ключом для проверки JWT. Храните его как пароль. На ноде положите файл в /opt/remnanode/docker-compose.yml:

services:
  remnanode:
    container_name: remnanode
    hostname: remnanode
    image: remnawave/node:3.4.1
    network_mode: host
    restart: always
    cap_add:
      - NET_ADMIN
    ulimits:
      nofile:
        soft: 1048576
        hard: 1048576
    environment:
      - NODE_PORT=2222
      - SECRET_KEY="ключ_из_панели"

Панель подставляет тег latest — замените его на явную версию: latest на каждом сервере означает то, что было свежим при его последнем pull, и парк незаметно разъезжается по версиям. NET_ADMIN нужен плагинам ноды на nftables, включая Torrent Blocker, и сбросу соединений удалённых пользователей.

cd /opt/remnanode && docker compose up -d && docker compose logs -f -t

В панели нажмите Next, выберите профиль и включите на ноде инбаунд VLESS_REALITY. Через несколько секунд карточка ноды покажет версии Xray и ноды.

Файрвол ноды

NODE_PORT открывайте только для IP панели. Нода работает в сети хоста, поэтому правила ufw действуют на неё напрямую:

ufw allow 22/tcp
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow from 203.0.113.10 to any port 2222 proto tcp
ufw enable

203.0.113.10 — адрес сервера панели, порт 80 нужен Caddy для выпуска и продления сертификата.

Совет: у ноды есть учёт трафика с лимитом и днём сброса. Панель считает трафик пользователей один раз, а в пакет ForestsNet идёт только исходящий, поэтому лимит можно ставить примерно равным пакету — без деления пополам, которое документация советует для хостеров, считающих оба направления.

Шаг 7. Хосты и сквады для нескольких локаций

Хост — клиентская сторона инбаунда: адрес, порт, SNI и подпись, которые пользователь увидит в приложении. Для каждой ноды заведите хост в Hosts: инбаунд VLESS_REALITY, адрес nl1.example.com, порт 443, а в расширенных настройках включите Переопределить SNI из адреса — иначе клиент получит первый элемент serverNames. Выбор нод в форме хоста — только метка для администратора.

Доступ задают внутренние сквады (Internal Squads) — наборы инбаундов из любых профилей. Пользователь получает хосты инбаундов из своих сквадов и заводится в Xray на всех нодах, где эти инбаунды включены. Единица доступа — инбаунд, а не нода: если одни клиенты должны видеть только Нидерланды, а другие ещё и Германию, заведите по профилю с собственным инбаундом на локацию и соберите из них сквады вроде Base и Premium. Внешние сквады (External Squads) переопределяют для группы пользователей шаблоны и настройки подписки.

Шаг 8. Пользователи и подписки

У пользователя задаются лимит трафика и стратегия сброса: NO_RESET, DAY, WEEK (по понедельникам), MONTH (1-го числа) или MONTH_ROLLING — ежемесячно в день создания пользователя, удобно для подписок с произвольной датой оплаты. Там же — дата окончания и сквады. Статусы: ACTIVE, DISABLED, LIMITED (трафик исчерпан) и EXPIRED; неактивный пользователь вместо серверов получает заглушки вроде «Subscription expired», тексты меняются в Subscription → Settings. Множитель трафика в карточке ноды позволяет считать трафик дорогой локации, например, вдвойне.

Лимит устройств по HWID (Subscription → Settings → HWID Device Limit) по умолчанию выключен. HWID отправляют не все приложения — по документации это Happ, v2RayTun, Koala Clash и FlClashX, — и после включения клиент без HWID получит пустую подписку или заглушку «App not supported», если лимит для пользователя не отключён.

Формат ответа выбирают правила ответа (Response Rules) по заголовкам запроса. По умолчанию браузер получает страницу подписки, клиенты на Mihomo, Stash, sing-box и Clash — конфиг по шаблону из Templates, остальные — base64-список ссылок. Отдельным правилом можно выдать свой шаблон конкретному приложению.

Приложения по платформам:

  • Android: v2rayNG и Happ (Xray), Hiddify (sing-box), FlClashX (Mihomo).
  • iOS и macOS: Happ, Streisand и V2Box (Xray), Shadowrocket, Stash, sing-box.
  • Windows, Linux и macOS: v2rayN (Xray), Clash Verge и Koala Clash (Mihomo), Hiddify и Throne (sing-box).

Шаг 9. Эксплуатация

Бэкап PostgreSQL

В базе лежит всё: пользователи с их UUID, профили, хосты, настройки и ключи удостоверяющего центра, которому доверяют ноды. Потеря базы без копии — это новый SECRET_KEY на каждой ноде и новые ключи у всех пользователей. Вместе с дампом храните .env: с другим APP_SECRET выпущенные API-токены перестанут приниматься.

/usr/local/bin/rw-backup.sh:

#!/bin/sh
set -e
D=/opt/remnawave/backup
mkdir -p "$D"
docker exec remnawave-db pg_dump -U postgres -d postgres -Fc > "$D/rw-$(date +%F-%H%M).dump"
cp /opt/remnawave/.env "$D/env-$(date +%F)"
find "$D" -type f -mtime +14 -delete
chmod +x /usr/local/bin/rw-backup.sh
echo "20 3 * * * root /usr/local/bin/rw-backup.sh" > /etc/cron.d/rw-backup

Копию уносите с сервера, см. Бэкапы баз данных. Восстанавливайте на ту же версию панели: поднимите только базу, залейте дамп, запустите остальное.

cd /opt/remnawave && docker compose up -d remnawave-db
docker exec -i remnawave-db pg_restore -U postgres -d postgres --clean --if-exists < backup/rw-2026-09-30-0320.dump
docker compose up -d

Обновления

Сначала панель, потом ноды:

cd /opt/remnawave && docker compose pull && docker compose down && docker compose up -d

Тег remnawave/backend:3 сам не перепрыгнет на следующую мажорную версию, и это к лучшему: в 3.0 пользователи в REST API стали адресоваться числовым id вместо UUID, и боты на старом API сломались. Перед мажорным обновлением читайте релиз и снимайте дамп. Ноды обновляйте по одной: сменили тег, docker compose pull && docker compose up -d, проверили версию в карточке — к следующей.

Логи и мониторинг

  • Панель: docker compose logs -f remnawave, процессы — docker exec -it remnawave pm2 monit, живость — curl -fsS http://127.0.0.1:3001/health.
  • Нода: docker logs -f remnanode — лог ноды, docker exec remnanode xlogs — лог Xray. Если пишете логи Xray на хост в /var/log/remnanode, настройте logrotate, иначе access-лог съест диск.
  • Метрики: http://127.0.0.1:3001/metrics с basic auth из METRICS_USER и METRICS_PASS. Для алертов пригодятся remnawave_node_status (1 — нода на связи) и remnawave_node_online_users, готовый дашборд Grafana — 25064.
  • Уведомления: о падении нод сообщит Telegram-бот (IS_TELEGRAM_NOTIFICATIONS_ENABLED, TELEGRAM_BOT_TOKEN, TELEGRAM_NOTIFY_NODES) или вебхук (WEBHOOK_ENABLED, WEBHOOK_URL).

Миграция с Marzban

Официальный путь — утилита remnawave/migrate v2.1.0 (Marzban, Marzneshin, 3X-UI). Она переносит пользователей с UUID VLESS, паролями Trojan и Shadowsocks, лимитами и сроками. Ноды поднимите заранее. Если старая схема уже стояла на своём домене, сохраните адреса, порты, ключ Reality, SNI и shortId — клиенты продолжат работать без обновления подписки; конфиги с чужой целью придётся перевыпустить. VMess в Remnawave нет. Об исходной панели — в статье Marzban: установка панели и нод.

./remnawave-migrate --panel-type=marzban \
  --panel-url=https://marzban.example.com --panel-username=admin --panel-password='пароль' \
  --remnawave-url=https://panel.example.com --remnawave-token='API-токен' \
  --dest-headers="Cookie:rw_gate=значение_RW_KNOCK" \
  --internal-squad=UUID_сквада --preserve-status --preserve-subhash --last-users=5

--dest-headers проводит утилиту через «стук» из шага 3. Утилита вышла до панели 3.0, поэтому начните с пробного прогона --last-users=5. Старые ссылки вида /sub/<токен> обслужит страница подписки: добавьте в её .env MARZBAN_LEGACY_LINK_ENABLED=true, CUSTOM_SUB_PREFIX=sub и MARZBAN_LEGACY_SECRET_KEY — результат SELECT secret_key FROM jwt LIMIT 1; в базе Marzban, а в .env панели — SUB_PUBLIC_DOMAIN=sub.example.com/sub.

Диагностика

Нода не подключается

  • Порт закрыт. С сервера панели: curl -vk --max-time 5 https://<IP ноды>:2222. Ошибка TLS — порт открыт и отвечает нода; таймаут — ufw, файрвол провайдера или неверный порт в карточке.
  • Испорчен SECRET_KEY. Ключ обрезался или получил перенос строки — нода падает с SECRET_KEY contains invalid JSON или Invalid SECRET_KEY payload. Скопируйте compose из панели заново.
  • Панель переустановили без базы. Удостоверяющий центр новый — выпустите SECRET_KEY каждой ноде заново.
  • Не стартует Xray. Нода на связи, но инбаунды мертвы, в логе ноды — «Xray Core … failed to start» и хвост лога Xray (полностью — docker exec remnanode xlogs). Почти всегда это ошибка в профиле или занятый порт 443.
  • Расходится время. mTLS и JWT чувствительны к часам: проверьте timedatectl на обеих сторонах.

Подписка отдаёт пусто или заглушки

  • «No hosts found», «Check Hosts tab», «Check Internal Squads tab». В сквадах пользователя нет инбаунда с включённым хостом, либо хост выключен, скрыт или ограничен другими сквадами. Хост выдаётся, а подключения нет — инбаунд не включён на ноде.
  • «Subscription expired», «limited», «disabled». Это статус пользователя, а не поломка.
  • Пустой ответ или «App not supported». Включён лимит HWID, а приложение не присылает HWID; «Limit of devices reached» — исчерпан лимит устройств.
  • 404. Пользователя с таким shortUuid нет: удалён или ссылка перевыпущена.
  • Ошибка у всех сразу. docker logs remnawave-subscription-page: истёк API-токен или в REMNAWAVE_PANEL_URL стоит https://.
  • Ссылки в панели ведут не туда. SUB_PUBLIC_DOMAIN с протоколом или слешем, либо контейнер не пересоздан после правки .env.

Reality не работает у части операторов

Сначала подключитесь тем же клиентом не из России: не работает и там — дело в ноде или профиле. Если проблема только у российских абонентов:

  • Первые 14–25 КБ приходят, потом тишина. С июня 2025 года ТСПУ у многих провайдеров замораживает TCP к подсетям крупных хостеров и CDN. XHTTP и смена поддомена тут не спасают, помогает промежуточный сервер в России. Действует ли ограничение у конкретного провайдера, покажет чекер TCP 16-20 из hyperion-cs/dpi-checkers.
  • Тяжёлые страницы замирают на пару минут. У части мобильных операторов серия TLS-рукопожатий к одному серверу замораживает новые соединения, а Vision открывает по рукопожатию на каждое соединение приложения. Помогают второй инбаунд на XHTTP и отпечаток firefox в расширенных настройках хоста.
  • Не открывается совсем, только на мобильном. Белые списки: оператор пропускает лишь разрешённые IP-диапазоны, и зарубежный VPS недоступен никаким протоколом. Нужен входной сервер в российском облаке — это отдельная схема.
  • Отвалилась одна нода при живых соседях. Бан адреса или SNI: смените поддомен selfsteal, а если не помогло — адрес; при бане адреса ForestsNet пишите в поддержку.

Команды для проверки из сети абонента разобраны в статье 3x-ui: установка и настройка VLESS + Reality, выбор протокола — в статье Свой VPN на VPS: какой протокол выбрать.

Если рукопожатие не проходит у всех клиентов ноды, это не DPI: SNI хоста не входит в serverNames, не отвечает цель (на ноде: curl -vk --resolve nl1.example.com:9443:127.0.0.1 https://nl1.example.com:9443) или хост привязан к инбаунду другого профиля с другим ключом.

502 от reverse proxy

  • Панель не запущена или перезапускается. docker compose ps в /opt/remnawave и curl -fsS http://127.0.0.1:3001/health; частая причина цикла — пароль базы, сменённый после первого старта.
  • Прокси не в той сети. Caddy или nginx должен быть в remnawave-network, иначе имя remnawave не разрешится.
  • Нет заголовков прокси. Без X-Forwarded-For и X-Forwarded-Proto: https бэкенд рвёт соединения, в логе — «Reverse proxy and HTTPS are required». В nginx нужен proxy_set_header X-Forwarded-Proto $scheme и TLS на самом nginx: за Cloudflare в режиме Flexible схема будет http.
  • Панель на подпути. location /panel не поддерживается — только отдельный домен или поддомен.

Источники