Локально WebSocket работает:.
Ниже — разделы про типичная ситуация, почему WebSocket требует особой настройки и минимальный конфиг, чтобы быстро понять устройство материала, практические ограничения и типовые точки отказа.
- Типичная ситуация
- Почему WebSocket требует особой настройки
- Минимальный конфиг
- Более аккуратный вариант с map
- ws:// и wss://
- Таймауты
- Node.js пример за Nginx
- FastAPI пример за Nginx
- Диагностика
- Проверить backend напрямую
- Проверить логи Nginx
- Проверить браузер
- Проверить протокол
- Частые ошибки
- Забыли proxy_http_version 1.1
- Не передали Upgrade
- Неправильный Connection
- Клиент подключается к неправильному path
- Сервер слушает внешний порт вместо localhost
- Мини-чеклист
- Ответы на эти вопросы могут быть для вас полезными
- Почему REST API работает, а WebSocket нет?
- Нужно ли открывать порт Node.js наружу?
- Что такое proxy_read_timeout?
- Почему нужен wss://?
- Можно ли проксировать несколько WebSocket endpoint?
- Что почитать дальше по WebSockets
Типичная ситуация
Локально WebSocket работает:
ws://localhost:3000
Вы выкатываете проект на сервер, открываете домен, а браузер пишет ошибку. REST API отвечает, страницы грузятся, но WebSocket не подключается
Частая причина — Nginx проксирует обычные HTTP-запросы, но не передает WebSocket Upgrade как надо
Почему WebSocket требует особой настройки
WebSocket начинается как HTTP-запрос, но затем соединение "апгрейдится" до WebSocket. Для этого нужны заголовки:
Upgrade;Connection;- HTTP/1.1 для proxy.
Если Nginx не передает эти заголовки backend-серверу, WebSocket handshake не проходит
Минимальный конфиг
Допустим, Node.js/FastAPI сервер слушает 127.0.0.1:3000, а WebSocket endpoint находится на /ws
Пример:
server {
listen 80;
server_name example.com;
location /ws {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_set_header Host $host;
}
}
Ключевые строки:
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
Без них обычные HTTP-запросы могут работать, а WebSocket — нет
Более аккуратный вариант с map
В официальной документации Nginx часто используют map, чтобы корректно задавать Connection
В блоке http:
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}
location /ws {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
}
Такой вариант лучше, если в конфиге есть разные сценарии соединений
ws:// и wss://
Если сайт открыт по HTTPS, клиент должен подключаться через:
wss://example.com/ws
А не:
ws://example.com/ws
Иначе браузер может заблокировать mixed content
Nginx в этом случае принимает TLS-соединение снаружи, а внутрь может проксировать на локальный backend:
location /ws {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
}
Снаружи для браузера это wss://example.com/ws, внутри для Nginx — обычный http://127.0.0.1:3000
Таймауты
WebSocket-соединение может быть долгим. Если Nginx закрывает его слишком быстро, добавьте таймауты:
location /ws {
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
Но таймауты не должны заменять нормальный heartbeat. В реальном приложении полезно периодически проверять живость соединения
Node.js пример за Nginx
Backend слушает локальный порт:
const { WebSocketServer } = require('ws');
const port = Number(process.env.PORT || 3000);
const wss = new WebSocketServer({ port });
wss.on('connection', (socket) => {
socket.send('connected');
});
console.log(`WebSocket server on ${port}`);
Снаружи клиент подключается не к :3000, а к домену:
const socket = new WebSocket('wss://example.com/ws');
Если Nginx location /ws проксирует на backend, путь должен совпадать с тем, как backend принимает соединение. Если backend слушает корень, иногда нужно аккуратно настроить path или proxy_pass
FastAPI пример за Nginx
FastAPI:
from fastapi import FastAPI, WebSocket
app = FastAPI()
@app.websocket("/ws")
async def websocket_endpoint(websocket: WebSocket):
await websocket.accept()
await websocket.send_text("connected")
Uvicorn:
uvicorn main:app --host 127.0.0.1 --port 8000
Nginx:
location /ws {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_set_header Host $host;
}
Клиент:
const socket = new WebSocket('wss://example.com/ws');
Диагностика
Проверить backend напрямую
Если есть доступ к серверу, проверьте локально:
curl http://127.0.0.1:3000
Для WebSocket лучше использовать специальный клиент, Postman или простой Node/Python-клиент
Проверить логи Nginx
Смотрите access/error logs. Если запрос до backend не дошел, это видно
Проверить браузер
В DevTools откройте Network и найдите WebSocket-запрос. Важно увидеть:
- какой URL реально используется;
- какой статус handshake;
- есть ли frames;
- какой close code.
Проверить протокол
На HTTPS-сайте должен быть wss://
Частые ошибки
Забыли proxy_http_version 1.1
Без HTTP/1.1 Upgrade может не пройти
Не передали Upgrade
Строка обязательна:
proxy_set_header Upgrade $http_upgrade;
Неправильный Connection
Для простого конфига:
proxy_set_header Connection "upgrade";
Для более аккуратного:
proxy_set_header Connection $connection_upgrade;
Клиент подключается к неправильному path
На backend /ws, в клиенте /socket, в Nginx /websocket — и все молча расходится. Приведите path к одной схеме
Сервер слушает внешний порт вместо localhost
Это не всегда ошибка, но на VPS часто backend держат на 127.0.0.1, а наружу открывают только Nginx
Мини-чеклист
Перед тем как копаться в коде приложения, проверьте:
- Клиент использует
wss://на HTTPS-домене. - Nginx передает
Upgrade. - Nginx передает
Connection. - Стоит
proxy_http_version 1.1. - Path совпадает.
- Backend запущен.
- Порт backend совпадает с
proxy_pass. - В логах backend видно подключение.
- В логах Nginx нет 400/502/504.
- Хостинг вообще поддерживает долгие соединения.
Ответы на эти вопросы могут быть для вас полезными
Почему REST API работает, а WebSocket нет?
Потому что WebSocket требует Upgrade handshake. Обычный HTTP proxy может пропускать REST и ломать WebSocket
Нужно ли открывать порт Node.js наружу?
Обычно нет. Часто наружу открыт Nginx на 80/443, а Node.js слушает localhost-порт
Что такое proxy_read_timeout?
Это время ожидания чтения от proxied-сервера. Для долгих WebSocket-соединений его часто увеличивают
Почему нужен wss://?
На HTTPS-странице браузер не должен подключаться к небезопасному ws://. wss:// — защищенный WebSocket
Можно ли проксировать несколько WebSocket endpoint?
Да. Делайте разные location, например /ws/chat и /ws/notifications, или маршрутизируйте внутри приложения
Что почитать дальше по WebSockets
Если вы собираете тему по шагам, рядом лучше открыть:
- WebSocket test online: как проверить соединение — отделить проблему proxy от проблемы приложения.
- WebSocket на JavaScript и Node.js: мини-чат в браузере — проверить proxy на простом Node.js WebSocket сервере.
- FastAPI WebSocket: уведомления в браузер — сверить настройки для FastAPI endpoint /ws.
- WebSocket простыми словами: когда нужен real-time — вернуться к handshake, ws:// и wss:// на уровне протокола.



