До основного вмісту

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

Nginx · 18.10.2026 · 4 хв читання
Ілюстрація до статті «WebSocket через Nginx: proxy_pass, Upgrade і таймаути»

WebSocket тримає одне TCP-з'єднання відкритим тривалий час і обмінюється кадрами в обидва боки без повторних HTTP-запитів — для чату, live-сповіщень чи адмін-панелі з оновленням у реальному часі. Nginx як зворотний проксі не підтримує Upgrade-з'єднання без явного налаштування: без нього запит на WebSocket просто не пройде далі звичного HTTP-проксі.

Чому WebSocket не працює через proxy_pass «з коробки»

Звичний proxy_pass у nginx налаштований на цикл HTTP-запит-відповідь, а WebSocket починається як HTTP-запит із заголовком Upgrade: websocket, після якого з'єднання повинне перейти у режим двостороннього обміну кадрами. Nginx за умовчанням не передає заголовок Upgrade на бекенд і тримає з'єднання за правилами звичного HTTP-проксі, що ламає перехід на протокол 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 пройшов успішно.

Чи була стаття корисною?
← Назад до бази знань Поставити питання підтримці