WebSocket через Nginx не переходит в 101: передаём Upgrade и Connection

Обычный HTTP к приложению через Nginx работает, а WebSocket не устанавливается. Клиент получает обычный ответ или ошибку handshake. Причина может быть в том, что заголовки переключения протокола относятся к одному сетевому участку. Прокси не передаёт их автоматически так же, как обычные end-to-end заголовки.

Добавьте явное переключение для нужного пути

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

server {
    listen 8080;
    server_name app.example;
    location /socket/ {
        proxy_pass http://127.0.0.1:9000;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;
    }
}

map располагается в контексте http, рядом с server, а не внутри location. Явная версия 1.1 делает поведение соединения к upstream понятным независимо от различий установленных версий Nginx. Если Upgrade присутствует, Connection получает upgrade; для обычного запроса используется close.

В этом примере путь /socket/ сохраняется, потому что proxy_pass не задаёт заменяющий URI. Backend должен обслуживать именно этот маршрут. Если приложение ожидает другой путь, настройте преобразование отдельно и проверьте его по журналу. Исправление Upgrade не устранит ошибочный URL.

Проверьте handshake, затем обмен сообщениями

Используйте штатный WebSocket-клиент приложения или браузерные инструменты разработчика. Успешный handshake для обычного HTTP/1.1-переключения даёт статус 101. Сравните прямое подключение к backend и подключение через Nginx, сохраняя путь, Origin и необходимые параметры авторизации.

Если backend напрямую возвращает 101, а через прокси — 400, сопоставьте заголовки на входе приложения. Если оба отвечают 403, проверьте авторизацию и разрешённые Origin. WebSocket не отменяет политику приложения, и принудительная подмена Origin ради зелёной проверки может скрыть реальную причину.

Разберите разрыв после успешного входа

Статус 101 ещё не подтверждает длительную работу канала. Nginx по умолчанию имеет ограничение ожидания чтения от upstream. Если приложение долго ничего не отправляет, соединение может закрыться. Увеличьте proxy_read_timeout для подходящего сценария либо организуйте протокольные ping-кадры со стороны сервера.

Таймаут задаёт интервал между операциями чтения, а не максимальную длительность всей сессии. Поэтому бесконечно большой параметр не заменяет контроль живого соединения. Подберите значение под ожидаемую активность и способность приложения обнаруживать отключённого клиента.

На стенде проверьте начало обмена, передачу сообщения в обе стороны и период без активности длиннее обычного рабочего интервала. Если перед Nginx есть ещё один балансировщик, у него могут быть собственные idle-таймауты. Запишите, какой участок закрыл канал, прежде чем менять все значения сразу.

После nginx -t и согласованного применения повторите проверку обычного HTTP на соседних путях. Ограничение WebSocket-настроек отдельным location помогает сохранить понятное поведение остального приложения.

Научитесь разбирать HTTP-путь по участкам. В курсе Linux и Bash для DevOps-инженеров есть диагностика сети, портов и HTTP. Откройте программу и продолжите практику на учебном сервисе: от установления соединения до проверки поведения приложения.

Ещё по теме

Продолжить на практике

  • Linux и Bash для DevOps-инженеров ↗

    Linux для работы с серверами: процессы, права, systemd, сеть, диски и логи. Автоматизируйте повседневные задачи на Bash и разберите типичные сбои сервиса — от занятого порта до переполненного диска.