Гайд: Панель, XHTTP и REALITY на одном сервере с портом 443

Зачем

Перейду к сути: развернуть сервер Xray легко, но настроить удобный софт вокруг него, поставить все на порт 443 и не закопаться в бесконечных конфигах nginx адски сложно. Поэтому предоставляю свои конфиги чтобы другие не мучались как я.

Под конец 2025 года РКН начали блокировать защитный слой REALITY в некоторых частях России, поэтому надо переходить на другой транспорт, который пока что работает - XHTTP. Но полностью заменять им старый конфиг не хотелось бы, так как протокол относительно новый и по моим наблюдениям иногда работает менее стабильно чем VLESS. Ну и короче я их объединил.

Гайд не для чайников

Nginx открывает порт 443, и в зависимости от поддомена перенаправляет трафик либо на панель (PasarGuard, Marzban, x-ui итп), либо на VLESS+XHTTP, либо на VLESS+REALITY.

Выглядит это все вот так:

xray.config

{
  "log": {
    "loglevel": "info"
  },
  "inbounds": [
    {
      "tag": "VLESS TCP REALITY",
      "listen": "127.0.0.1",
      "port": 13000,
      "protocol": "vless",
      "settings": {
        "clients": [],
        "decryption": "none"
      },
      "streamSettings": {
        "network": "raw",
        "rawSettings": {
          "acceptProxyProtocol": false
        },
        "security": "reality",
        "realitySettings": {
          "show": false,
          "dest": "example.com:443",
          "xver": 0,
          "serverNames": [
            "example.com"
          ],
          "privateKey": "<private key>",
          "publicKey": "<public key>",
          "shortIds": [
            "<short id>"
          ]
        }
      },
      "sniffing": {
        "enabled": true,
        "destOverride": [
          "http",
          "tls",
          "quic"
        ]
      }
    },
    {
      "tag": "VLESS XHTTP",
      "listen": "127.0.0.1",
      "port": 14000,
      "protocol": "vless",
      "settings": {
        "clients": [],
        "decryption": "none"
      },
      "streamSettings": {
        "network": "xhttp",
        "xhttpSettings": {
          "mode": "auto",
          "path": "/xhttp"
        }
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "tag": "DIRECT"
    },
    {
      "protocol": "blackhole",
      "tag": "BLOCK"
    }
  ],
  "routing": {
    "rules": [
      {
        "ip": [
          "geoip:private"
        ],
        "outboundTag": "BLOCK",
        "type": "field"
      }
    ]
  }
}

/etc/nginx/nginx.conf

user nginx;
worker_processes auto;

error_log /var/log/nginx/error.log notice;
pid /run/nginx.pid;

events {
    worker_connections 1024;
}

stream {
    # --- Upstreams ---
    upstream backend_xhttp {
        server 127.0.0.1:1443;
    }
    upstream backend_reality {
        server 127.0.0.1:13000;
    }
    upstream backend_panel {
        server 127.0.0.1:8000; # Порт панели
    }

    # --- Routing ---
    map $ssl_preread_server_name $target_backend {
        example.com                 backend_reality;      # VLESS
        panel.example.com           backend_panel;        # Панель
        connection.example.com      backend_xhttp;        # XHTTP
        default                     backend_xhttp;        # Fallback
    }

    # --- Server ---
    server {
        listen 443 reuseport;
        listen [::]:443 reuseport;

        ssl_preread on;
        proxy_pass $target_backend;
    }
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;

    # Logging
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
    access_log /var/log/nginx/access.log main;

    # Настройки
    sendfile on;
    keepalive_timeout 65;
    client_max_body_size 0;

    # Конфиги
    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*.conf;
}

/etc/nginx/sites-enabled/website.conf

# --- XHTTP (порт 1443) ---
server {
    listen 127.0.0.1:1443 ssl http2 ipv6only=off reuseport;
    listen 127.0.0.1:1443 quic ipv6only=off reuseport;

    server_name connection.example.com;
    root /var/www/html;
    index index.html;

    include /etc/nginx/snippets/ssl-params.conf;

    client_header_timeout 5m;
    keepalive_timeout 5m;

    location /xhttp {
        grpc_pass grpc://127.0.0.1:14000;

        grpc_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        grpc_read_timeout 315;
        grpc_send_timeout 5m;
    }
}

/etc/nginx/snippets/ssl-params.conf

ssl_certificate /path/to/fullchain.pem;
ssl_certificate_key /path/to/priv.key;

ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;

ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;

Также обязательно установить специальную версию Nginx для поддержки QUIC (для Ubuntu 24.04):

sudo apt install -y curl gnupg2 ca-certificates lsb-release ubuntu-keyring
curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor \
    | sudo tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null
echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] \
http://nginx.org/packages/mainline/ubuntu `lsb_release -cs` nginx" \
    | sudo tee /etc/apt/sources.list.d/nginx.list
echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" \
    | sudo tee /etc/apt/preferences.d/99nginx
sudo apt update
sudo apt install nginx
sudo systemctl daemon-reload
sudo systemctl restart nginx

Для хоста XHTTP в своей админ панели надо поставить следующее:

Address = поддомен для XHTTP
Security = TLS
Порт = 443

В Marzban и PasarGuard:

Результат

Работает и REALITY, и XHTTP, и панелька на порте 443:

Если будут ошибки в терминологии - пишите. С другими ошибками (aka skill issue) помогать не буду. Но если получу много запросов то сделаю полноценный гайд тут или на хабре.

sourcelocation, сколько ОЗУ на VPS потребляет всё это хозяйство?

суммарно 1.3GB. Так много исключительно потому, что юзаю PasarGuard. Но если много озу то не страшно

  • админ панель PasarGuard и все файлы python - 539MB
  • postgres - 260MB
  • docker - еще около 200MB
  • nginx - 25-30MB

Возможно ли так сделать несколько инбаундов vless’а чтобы тоже были на 443 порту? разные порты внутри у них будут и разные SNI

в теории да

а на практика?)

не пробовал, но можно аналогично добавить дополнительные upstream’ы для каждого нового inbound’а

Однако жирно. Я ваял когда то vless на чистом ядре Xray и там, насколько сейчас помню, вместе с бубунтовым сервером было занято RAM что то около 300 MB ±, впс-ка была на 512 MB RAM.

всё будет работать, на каждый sni свой апстрим

зачем использовать реалити если вы уже подняли веб сервер который умеет в тлс? я полагаю это в разы легитимнее самого реалити. и ладно бы реалити придумали воровать чужой публичный серт, так зачем вам воровать свой публичный серт у себя самого и потом шифровать трафик по публичному и приватному ключу?
чем плох влесс без шифрования за веб сервером со своим шифрованием? точно так же вешается на кастомный путь, делов то

я не эксперт, но у него насколько вижу в конфиге на nginx имеется домен(может и не настоящий, но разграничение идет не по путям, а по псевдосни смотрите блок routing, у меня например настроено по секретным путям), поэтому он правильно делает, что дест указывает себя самого, reality используют на других сайтах для подмены sni под какой-то конкретный например из белого списка раньше работало до октября примерно уходящего года, либо когда нет своего домена, и чтобы выглядел трафик более нормально, при активном зондировании. можете почитать статью про reality известную, автор рекомендует под себя маскироваться с доменом. еще я думаю вам стоит почитать про саму концепцию fallback, чтобы не было недопонимания.

На моей VPS аналогичный конфиг, но без Reality и без QUIC потребляет чуть меньше 200МБ. Если убрать роутинг по dat файлам будет еще меньше. Сейчас использую geoip_RU.dat:ru, geoip_RU.dat:ru-blocked, geosite_RU.dat:ru-blocked.
nginx 19MB
x-ui 70MB
xray-core 104MB

nginx хостит простенький сайт на mydomain.com.
Коннекты на поддомен data.mydomain.com перенаправляются на уровне TCP по unix сокету на VLESS inbound, где VLESS расшифровывает TLS по letsencrypt сертификатам поддомена и выполняет проверку свой/чужой по PATH (https://data.mydomain.com/secret-example-path-vless-xhttp).
По моим тестам VLESS заметно шустрее справляется с TLS шифрованием чем nginx.

в nginx помимо sendfile on у меня настроено:
tcp_nodelay on;
tcp_nopush off;
gzip off;

Насколько целесообразно использовать QUIC для XHTTP? Разницу, наверно, только в играх можно ощутить?

у вас xray с панелью, не голое ядро?

С панелью.
Сейчас пока панель закрыта и нет клиентов: nginx 19МБ + x-ui 65МБ + xray-core 60МБ = 144МБ