К основному содержимому

WebSocket через Nginx: proxy_pass, Upgrade и таймауты

Nginx · 18.10.2026 · 4 мин чтения
Иллюстрация к статье «WebSocket через Nginx: proxy_pass, Upgrade и таймауты»

WebSocket держит одно TCP-соединение открытым длительное время и обменивается кадрами в обе стороны без повторных HTTP-запросов — для чата, live-уведомлений или админ-панели с обновлением в реальном времени. Nginx как обратный проксi не поддерживает Upgrade-соединения без явной настройки: без неё запрос на WebSocket просто не пройдёт дальше обычного HTTP-прокси.

Почему WebSocket не работает через proxy_pass «из коробки»

Обычный proxy_pass в nginx настроен на цикл HTTP-запрос-ответ, а WebSocket начинается как HTTP-запрос с заголовком Upgrade: websocket, после которого соединение должно перейти в режим двустороннего обмена кадрами. Nginx по умолчанию не прокидывает заголовок Upgrade на бэкенд и держит соединение по правилам обычного HTTP-proxy, что ломает переход на протокол WebSocket.

Базовая настройка location и proxy_pass для сайта на VDS не меняется и описана в статье про настройку Nginx. Для WebSocket к ней добавляются всего несколько директив, но без них соединение рвётся сразу после handshake.

Настройка map и заголовков Upgrade/Connection

Рекомендованная в документации nginx схема — объявить map, который превращает значение заголовка Upgrade в значение заголовка Connection: если Upgrade присутствует, Connection становится upgrade, а если заголовка нет (обычный HTTP-запрос к тому же location), Connection закрывается явно.

Сам location получает proxy_http_version 1.1 — HTTP/1.0 не поддерживает Upgrade, — и оба заголовка прокидываются на бэкенд через proxy_set_header.

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

server {
    location /ws/ {
        proxy_pass http://127.0.0.1:8080;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
        proxy_read_timeout 3600s;
        proxy_send_timeout 3600s;
    }
}

Таймауты: почему соединение рвётся через минуту

Здесь важны три директивы, и их значения по умолчанию в nginx слишком малы для долгоживущего соединения.

ДирективаЗначение по умолчаниюЗачем менять для WebSocket
proxy_read_timeout60 секундразрывает соединение, если от бэкенда нет данных дольше этого времени
proxy_send_timeout60 секундто же самое, но для отправки данных на бэкенд
proxy_connect_timeout60 секундна долгоживущее соединение влияет меньше, но тоже стоит проверить

Если после увеличения таймаутов соединение всё равно обрывается с кодом 502 или 504, причину стоит искать по статье про диагностику 502 и 504 — чаще всего дело не в таймауте, а в самом бэкенде.

WebSocket-сервер на стороне приложения

Сам WebSocket-сервер, который принимает соединение после nginx, — отдельное приложение, а не часть PHP-FPM: обычный php-fpm обрабатывает один запрос и завершает процесс, что не подходит для долгоживущего соединения.

Пример такого сервера на PHP с библиотекой Ratchet разобран в статье про WebSockets на PHP с Ratchet. Nginx в этой схеме проксирует соединение на порт, который слушает именно этот процесс, а не php-fpm.

Чек-лист перед запуском WebSocket за nginx

Перед тем как выводить WebSocket-соединение в продакшен через nginx, стоит проверить следующее.

  • proxy_http_version установлен в 1.1, иначе Upgrade не пройдёт даже с правильными заголовками.
  • map для Connection учитывает оба случая — с Upgrade и без него, для location, который обслуживает и обычные HTTP-запросы, и WebSocket.
  • proxy_read_timeout и proxy_send_timeout увеличены настолько, чтобы не рвать соединения чата или live-обновлений раньше времени.
  • На стороне приложения реализован ping/pong или другой механизм проверки живого соединения, а не только надежда на таймауты nginx.
  • Балансировка между несколькими WebSocket-процессами, если она есть, учитывает липкость сессии — клиент не должен прыгать между процессами посреди соединения.

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

Нужен ли отдельный server-блок для WebSocket?

Нет, обычно достаточно отдельного location в том же server-блоке, что обслуживает основной сайт — например /ws/. Полноценный HTTP-сайт и WebSocket-эндпоинт спокойно живут в одном конфиге, если заголовки Upgrade и Connection настроены именно для нужного location.

Что если за nginx стоит ещё и балансировщик нагрузки?

Балансировщик перед nginx должен сам поддерживать проксирование Upgrade-соединений и не обрывать долгоживущие TCP-сессии по собственным таймаутам — иначе настройка на уровне nginx не поможет, проблема окажется на уровень выше.

Как проверить, что WebSocket действительно работает через nginx?

Проще всего — через curl с заголовками, имитирующими handshake, или через браузерную консоль разработчика, где виден статус соединения и обмен кадрами. Код ответа 101 Switching Protocols в логах nginx подтверждает, что переход на WebSocket прошёл успешно.

Статья помогла?
← Назад в базу знаний Задать вопрос поддержке