Новинка Виртуальный VDS сервер в Нидерландах от 390р
Выберите продукт

WebSocket через Nginx на VDS: proxy, Upgrade/Connection, таймауты и SSL‑терминация

Разбираем, как проксировать WebSocket через Nginx на VDS: базовая схема, корректные заголовки Upgrade/Connection, тонкая настройка таймаутов, SSL‑терминация и отдельные логи. Покажем диагностику 101/502/504, балансировку и меры безопасности.
WebSocket через Nginx на VDS: proxy, Upgrade/Connection, таймауты и SSL‑терминация

В реальных веб‑проектах WebSocket чаще всего прячут за обратным прокси — и именно Nginx остается стандартом де‑факто. На VDS такой дизайн дает контроль над SSL‑терминацией, нагрузкой и безопасностью, не усложняя приложение. Ниже — практическое руководство: рабочие конфиги, нюансы заголовков Upgrade/Connection, таймауты для долгоживущих соединений и типовые ошибки. Если вы как раз выбираете сервер, обратите внимание на VDS.

Как работает WebSocket за Nginx

WebSocket начинается с HTTP‑рукопожатия: клиент отправляет запрос с Upgrade: websocket и Connection: Upgrade, а сервер отвечает 101 Switching Protocols. После этого канал становится двунаправленным, без дальнейших HTTP‑заголовков. Задача Nginx — корректно пробросить это рукопожатие к бэкенду и не разорвать длительное соединение преждевременно.

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

  • proxy_http_version 1.1 — для рукопожатия WebSocket;
  • проброс заголовков Upgrade и Connection с учетом их отсутствия;
  • отключение буферизации для двунаправленного канала;
  • увеличенные таймауты чтения;
  • правильная SSL‑терминация (если клиент подключается по wss://).

Базовая схема и минимальный конфиг

Предположим, приложение слушает 127.0.0.1:3000, а клиенты будут подключаться к /ws/. Начнем с блока http и map для безопасного формирования заголовка Connection:

http {
    map $http_upgrade $connection_upgrade {
        default upgrade;
        ''      close;
    }

    upstream ws_backend {
        server 127.0.0.1:3000;
        keepalive 64;
    }

    server {
        listen 80;
        server_name example.com;

        location /ws/ {
            proxy_http_version 1.1;
            proxy_set_header Upgrade $http_upgrade;
            proxy_set_header Connection $connection_upgrade;
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;

            proxy_pass http://ws_backend;

            proxy_read_timeout 1h;
            proxy_send_timeout 1m;
            proxy_connect_timeout 5s;

            proxy_buffering off;
            proxy_request_buffering off;
        }
    }
}

Комментарии:

  • map подставляет Connection: upgrade, только если клиент прислал Upgrade. Иначе — close, что предотвращает ложные апгрейды.
  • proxy_http_version 1.1 обязателен, иначе WebSocket‑апгрейд не произойдет.
  • proxy_buffering off исключает буферизацию данных — для двунаправленного канала это критично.
  • proxy_read_timeout увеличен до часа: без трафика соединение не падает. Конкретное значение выбирайте под вашу модель трафика и пинги приложения.
FastFox VDS
Облачный VDS-сервер
Виртуальные серверы с быстрым запуском и гибкой конфигурацией от 390₽ / мес
Доступные локации
Россия Нидерланды

SSL‑терминация и WSS

В типичной схеме Nginx завершает TLS на 443 и проксирует незашифрованным каналом к локальному бэкенду. Клиент подключается по wss://, а бэкенд по ws:// (через proxy_pass http://...). Это упрощает управление сертификатами и шифросетами, а также разгружает приложение. Для выпуска и продления удобно использовать SSL‑сертификаты.

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com;

    ssl_certificate /etc/ssl/example.crt;
    ssl_certificate_key /etc/ssl/example.key;

    # Важно: WebSocket рукопожатие пойдет по HTTP/1.1 даже при включенном http2

    location /ws/ {
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_pass http://ws_backend;

        proxy_read_timeout 1h;
        proxy_send_timeout 1m;
        proxy_connect_timeout 5s;

        proxy_buffering off;
        proxy_request_buffering off;
    }
}

Даже с listen ... http2; браузеры по‑прежнему выполняют WebSocket‑апгрейд как HTTP/1.1 — стороне Nginx это не мешает. Для перехода всего сайта на HTTPS с редиректом и HSTS может пригодиться разбор: миграция на HTTPS, 301 и HSTS.

Конфигурация Nginx для WebSocket: map, заголовки Upgrade/Connection и таймауты

Таймауты и keepalive: что влияет на обрыв

WebSocket‑соединения долгоживущие; стандартные таймауты Nginx часто слишком малы. Сфокусируемся на ключевых директивах и системных настройках.

  • proxy_read_timeout — сколько Nginx будет ждать данных от бэкенда, не закрывая соединение. Для «тихих» соединений ставят минуты или часы; если приложение шлет пинги, можно короче.
  • proxy_send_timeout — таймаут отправки в бэкенд. Обычно менее критичен для WS, но полезен при сетевых проблемах.
  • proxy_connect_timeout — время установления TCP‑соединения к бэкенду. При превышении клиент быстро узнает об ошибке и переподключится.
  • keepalive в upstream — число держимых открытых TCP‑соединений к бэкендам для повторного использования (не путать с WebSocket).
  • keepalive_timeout в контексте http — влияет на клиентские HTTP keep‑alive до апгрейда; к уже апгрейженному каналу не относится напрямую.

Проверяйте таймауты по всей цепочке: CDN, балансировщик, Nginx, приложение, БД и очереди. Самый короткий из них оборвет соединение.

  • Если приложение отправляет пинги (WebSocket ping/pong) каждые 30–60 секунд — proxy_read_timeout 2m достаточно. Если нет — поднимайте до 10–60 минут.
  • На уровне ОС полезно согласовать TCP‑keepalive с пингами приложения.
  • Следите, чтобы таймауты БД/брокеров сообщений не были короче ожиданий в приложении.

Логи и диагностика: видим 101, 502, 504

Для разбора инцидентов удобен отдельный формат логов с кодами апстрима и заголовками апгрейда:

log_format ws '$remote_addr - $host $request $status $upstream_status '
               'ua=$http_user_agent upg=$http_upgrade conn=$http_connection '
               'rt=$request_time urt=$upstream_response_time';

access_log /var/log/nginx/access_ws.log ws;

На что смотреть:

  • 101 в $status и $upstream_status — рукопожатие ок.
  • 502 — бэкенд недоступен или упал; проверьте proxy_pass, firewall, upstream, логи приложения.
  • 504 — таймаут ожидания от бэкенда; увеличьте proxy_read_timeout и убедитесь, что приложение держит канал живым.
  • 400 — часто ошибка в заголовках Upgrade/Connection или несовместимый путь.
FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Масштабирование: несколько бэкендов и привязка сессий

WebSocket‑соединение «липкое»: одна сессия живет на одном инстансе. Для масштаба добавляют несколько серверов в upstream. Если нужно закрепление клиента за узлом (состояние в памяти), подойдет IP‑привязка:

upstream ws_backend {
    ip_hash;
    server 127.0.0.1:3001;
    server 127.0.0.1:3002;
    keepalive 128;
}

У ip_hash есть нюансы: неравномерность распределения и чувствительность к NAT. Альтернатива — перенос состояния в Redis/БД и отказ от липкости на уровне прокси. Не забудьте лимиты дескрипторов и worker_connections — WebSocket множит число одновременных соединений.

Схема балансировки WebSocket с ip_hash и несколькими бэкендами

Частые ошибки и их исправление

1) Неверные заголовки Upgrade/Connection

Симптом: соединение не апгрейдится, в логах 400. Решение: используйте map, как в примере, и проверьте proxy_http_version 1.1. Значения должны быть ровно Upgrade: websocket и Connection: upgrade.

2) Смешанный контент и переход на WSS

Если сайт открыт по HTTPS, браузер заблокирует ws://. Используйте wss:// и корректный сертификат. На стороне Nginx — SSL‑терминация, как в примере для 443.

3) 502/504 при простое

Классика для «немых» соединений: бэкенд или Nginx закрывают канал. Поднимайте proxy_read_timeout и реализуйте heartbeat. Проверьте сетевые idle‑политики.

4) Буферизация сломала поток

Забыли proxy_buffering off — двунаправленный обмен начнет «залипать». Выключайте буферизацию и для запросов (proxy_request_buffering off).

5) HTTP/2 и WebSocket

http2 на 443 — не проблема: браузер делает апгрейд по HTTP/1.1. Главное — proxy_http_version 1.1 к бэкенду. Не путайте это с SSE — у них иная модель.

Безопасность: минимум, который стоит включить

  • Проверяйте Origin на бэкенде. WebSocket не делает CORS как XHR/fetch.
  • Разделяйте пути: держите WS в отдельном location (/ws/), ограничивайте методы/заголовки.
  • Ограничения по ресурсам: ulimit, worker_rlimit_nofile, worker_connections под вашу нагрузку.
  • Логи и ротация: отдельный access_log для WS и агрессивная ротация.
  • Изоляция бэкенда: слушайте только 127.0.0.1 или внутренний интерфейс.

Пример end‑to‑end: Node.js ws за Nginx

Минимальный сервер на Node.js (библиотека ws):

// app.js
const WebSocket = require('ws');
const server = new WebSocket.Server({ host: '127.0.0.1', port: 3000 });
server.on('connection', socket => {
  socket.send('hello');
  socket.on('message', msg => {
    socket.send('echo: ' + msg);
  });
});
console.log('WS on 127.0.0.1:3000');

Nginx‑конфиг — как в начале статьи: location /ws/ с proxy_http_version 1.1, заголовками Upgrade/Connection и отключенной буферизацией. Проверка из консоли браузера:

const ws = new WebSocket('wss://example.com/ws/');
ws.onmessage = e => console.log(e.data);
ws.onopen = () => ws.send('ping');

Если вместо wss:// используете ws:// на 80‑м порту и в браузере открыт HTTPS‑сайт — подключение будет заблокировано политикой смешанного контента.

Чеклист перед продакшеном

  • Рукопожатие 101 проходит: в логах видно status=101, upstream_status=101.
  • Заголовки Upgrade/Connection прокинуты верно через map.
  • Включена SSL‑терминация, редирект с 80 на 443, сертификат актуален.
  • Выключена буферизация, увеличен proxy_read_timeout.
  • Лимиты файловых дескрипторов и worker_connections соответствуют ожидаемой нагрузке.
  • Логи вынесены в отдельный файл, настроена ротация.
  • Бэкенд слушает только локальный интерфейс, firewall закрыт для внешнего доступа к нему.

Итоги

Чтобы WebSocket стабильно работал за Nginx на VDS, достаточно нескольких точных настроек: HTTP/1.1 к апстриму, корректные Upgrade/Connection, отключенная буферизация и растянутые таймауты. SSL‑терминация на Nginx упрощает сертификаты (SSL), а логирование помогает оперативно ловить 101/502/504. Масштабирование через upstream и дисциплина системных лимитов позволяют выдерживать тысячи постоянных коннектов.

Поделиться статьей

Вам будет интересно

Nginx debug log на VDS: включаем отладку только для нужного IP OpenAI Статья написана AI (GPT 5)

Nginx debug log на VDS: включаем отладку только для нужного IP

Показываем, как на VDS включить подробный nginx debug log не для всего трафика, а только для вашего IP: проверить сборку Nginx, на ...
Netplan на Ubuntu VDS: статический IPv4/IPv6, default route, DNS и безопасное применение по SSH OpenAI Статья написана AI (GPT 5)

Netplan на Ubuntu VDS: статический IPv4/IPv6, default route, DNS и безопасное применение по SSH

Пошаговая инструкция по Netplan для Ubuntu VDS: как найти интерфейс, прописать static IP, IPv6, default route и DNS, проверить net ...
Linux TCP backlog на VDS: somaxconn, tcp_max_syn_backlog, Nginx и PHP-FPM OpenAI Статья написана AI (GPT 5)

Linux TCP backlog на VDS: somaxconn, tcp_max_syn_backlog, Nginx и PHP-FPM

Когда сайт получает всплеск соединений, узкое место может быть не в CPU, а в очередях TCP. Поясняем, как увидеть переполнение back ...