← Назад к кейсам Связаться
Кейс: Сетевая инженерия

Невидимый VPN-туннель:
Nginx SNI-маршрутизация

Мультиплексирование порта 443: как разместить скрытый Xray-туннель (VLESS + Reality) на сервере с боевым веб-сайтом, избежав конфликта портов и спрятав VPN-трафик от систем глубокого анализа (DPI/ТСПУ).

Nginx stream ssl_preread VLESS + Reality Xray-core 3x-ui SNI Routing

Один порт — два сервиса — ноль компромиссов

Исходная ситуация: На сервере работает боевой веб-сайт, обслуживающий HTTPS-трафик на стандартном порту 443. Необходимо развернуть на этом же сервере скрытый VPN-туннель на базе протокола VLESS + Reality (Xray-core), который также требует для своей работы порт 443.

Почему нельзя просто взять другой порт: Системы DPI/ТСПУ анализируют трафик и блокируют подозрительные TLS-соединения на нестандартных портах. Порт 443 — единственный, который гарантированно пропускается без лишних вопросов, поскольку на нём работает весь веб.

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

Nginx как Front-door швейцар

Ключевая идея — использовать модуль stream и директиву ssl_preread в Nginx. Nginx выступает единственным слушателем на публичном интерфейсе и маршрутизирует TLS-соединения до их расшифровки, анализируя только заголовок ClientHello (поле SNI).

01

Уровень L4: stream-прокси

Nginx принимает все входящие TCP-соединения на публичном IP:443. Модуль ssl_preread извлекает имя сервера (SNI) из первого TLS-пакета без терминации шифрования. Это позволяет принять решение о маршруте ещё до установления TLS-сессии.

02

Маршрутизация по SNI

Директива map задает карту маршрутов: запросы с SNI домена сайта направляются на внутренний порт веб-сервера (8443). Запросы с маскировочным SNI (например, oracle.com) проксируются на локальный процесс Xray. Сканеры и неизвестные запросы уходят на заглушку сайта.

03

Изоляция через Socket Binding

Nginx слушает 443 порт строго на внешнем публичном IP. Xray слушает тот же порт 443, но привязан исключительно к 127.0.0.1. Это устраняет конфликт сокетов и позволяет панели 3x-ui генерировать ссылки с портом 443 без ручных правок.

Конфигурация и ключевые параметры

/etc/nginx/nginx.conf — блок stream

Сердце архитектуры. Этот блок добавляется в главный конфигурационный файл Nginx на уровне top-level (вне http {}). Он обеспечивает L4-маршрутизацию на основе SNI.

stream {
    map $ssl_preread_server_name $backend_name {
        # Маскировочный SNI → отправляем в Xray
        www.oracle.com       xray_backend;
        oracle.com           xray_backend;

        # Личный сайт → локальный порт веб-сервера
        ragtagsky.xyz        web_backend;
        www.ragtagsky.xyz    web_backend;

        # Все остальные (сканеры, боты) → заглушка
        default              web_backend;
    }

    upstream xray_backend {
        server 127.0.0.1:443;   # VLESS-туннель (Xray)
    }

    upstream web_backend {
        server 127.0.0.1:8443;  # Веб-сайт (HTTPS)
    }

    server {
        listen :443;
        proxy_pass $backend_name;
        ssl_preread on;
    }
}

/etc/nginx/sites-available/default — привязка сайта

Веб-сервер перевешивается с публичного интерфейса на локальный порт 8443. Сертификаты Let's Encrypt остаются на месте — терминация TLS происходит именно здесь, а не в блоке stream.

server {
    listen 127.0.0.1:8443 ssl;

    ssl_certificate     /etc/letsencrypt/live/ragtagsky.xyz/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/ragtagsky.xyz/privkey.pem;
    include             /etc/letsencrypt/options-ssl-nginx.conf;
    ssl_dhparam         /etc/letsencrypt/ssl-dhparams.pem;

    # ... остальная конфигурация сайта ...
}

Параметры Inbound в 3x-ui (Xray-core)

Настройки входящего соединения (Inbound), позволяющие Xray принимать проксированный трафик от Nginx и маскировать его под легитимный TLS-хендшейк.

Привязка сокета

  • Протокол: VLESS
  • Порт: 443
  • Listen IP: 127.0.0.1
  • Стратегия адреса: Пользовательская → ragtagsky.xyz

Безопасность (Reality)

  • Транспорт: TCP (RAW)
  • Flow: xtls-rprx-vision
  • Безопасность: Reality
  • SNI / Dest: www.oracle.com:443
  • uTLS: firefox

⚠ Ключи аутентификации (UUID, Private/Public Key, ShortID) в кейсе опущены в целях безопасности.

Ключевой момент: разведение по интерфейсам

Главная ловушка при такой схеме — конфликт сокетов. Если оба процесса (Nginx и Xray) попытаются занять 0.0.0.0:443, второй получит ошибку EADDRINUSE. Решение:

  • Nginx слушает <YOUR_SERVER_IP>:443 — публичный интерфейс. Это единственная точка входа для внешнего трафика.
  • Xray слушает 127.0.0.1:443 — loopback-интерфейс. Принимает только трафик, перенаправленный Nginx локально.
  • Бонус: Именно такая привязка (127.0.0.1 + порт 443) позволяет панели 3x-ui генерировать правильные клиентские ссылки, поскольку в Inbound указан стандартный порт 443, а домен задан через «Пользовательскую стратегию адреса».

Что получилось в результате

Невидимость для DPI

VPN-трафик неотличим от обычного HTTPS. Системы глубокого анализа видят легитимный TLS-хендшейк с валидным SNI и не имеют оснований для блокировки.

Сайт работает штатно

Веб-сайт продолжает обслуживаться на порту 443 без каких-либо изменений в пользовательском опыте. Сертификаты Let's Encrypt обновляются как обычно.

Автоматические ссылки

Панель 3x-ui корректно генерирует клиентские конфиги с нужным портом и доменом. Добавление новых пользователей не требует ручных правок.

Один сервер — полный стек

Веб-сайт и VPN-туннель мирно сосуществуют на одном физическом сервере без необходимости выделять дополнительные IP-адреса или машины.

Результат: Получилась прозрачная и масштабируемая архитектура, где Nginx выступает единым контроллером входящего трафика. Система легко расширяется добавлением новых SNI-маршрутов — например, для отдельного Telegram-прокси или дополнительного VPN-протокола.

Нужна подобная настройка?

Спроектирую и настрою скрытый VPN-туннель на вашем сервере с учётом требований к безопасности и совместимости с существующими сервисами.