🔌
Хостинг

WebSocket на хостинге: обычный хостинг, VPS и настройка Nginx

13.11.2026
← Все статьи

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

Чем WebSocket отличается от обычного HTTP-запроса

Обычный сайт работает по принципу «запрос — ответ»: браузер запрашивает страницу, сервер выполняет PHP-скрипт, возвращает ответ, и соединение закрывается. WebSocket тоже начинается с HTTP-запроса, но в нём есть заголовок Upgrade: websocket. Если сервер согласен, он отвечает 101 Switching Protocols, после чего то же TCP-соединение остаётся открытым. Теперь и сервер, и браузер могут в любой момент отправлять друг другу сообщения.

Значит, для WebSocket на сервере нужен постоянно работающий процесс, который держит соединения. Обычно это отдельная программа на Node.js, Python или Go: она слушает свой порт и хранит в памяти тысячи открытых соединений. Именно это требование и сталкивается с устройством обычного хостинга.

Почему WebSocket не работает на обычном хостинге

На обычном (виртуальном) хостинге один сервер разделён между множеством клиентов. PHP-скрипты запускаются при поступлении запроса и должны завершиться за несколько секунд, время выполнения ограничено. Открыть собственный порт и принимать на него внешние подключения клиенту, как правило, нельзя, а постоянно работающие фоновые процессы могут останавливаться из-за ограничений ресурсов. Настройки веб-сервера также не в руках клиента, поэтому добавить правило проксирования, передающее заголовок Upgrade внутреннему приложению, не получится.

По этой же причине и PHP-библиотеки для WebSocket (например, Ratchet) нестабильны на обычном хостинге: им тоже нужны постоянный процесс и открытый порт. Если проект обязан остаться на обычном хостинге, работу «почти в реальном времени» придётся строить иначе.

Если нужно остаться на обычном хостинге

Во многих случаях для «живого» поведения WebSocket на самом деле не нужен. Например, для изменений в расписании на школьном сайте или статуса заказа в интернет-магазине достаточно, чтобы браузер раз в 5–10 секунд отправлял небольшой JSON-запрос. Такой периодический опрос (polling) работает на обычном хостинге без проблем, потому что каждый запрос короткий и выполняется как обычный PHP-скрипт.

Есть ещё long polling и Server-Sent Events, но они надолго занимают PHP-процесс для каждого пользователя. Если одновременно подключатся десятки человек, лимит процессов на хостинге быстро исчерпается, и сайт перестанет отвечать на другие запросы. Поэтому на обычном хостинге самый безопасный вариант — короткий периодический опрос, а полноценный WebSocket запускается на отдельном сервере.

Пример: экран заказов для кухни ресторана

Ресторан в Ташкенте хочет, чтобы официанты вводили заказ на планшете, а он сразу появлялся на экране на кухне. Даже задержка в пару секунд неудобна поварам, поэтому здесь WebSocket уместен. Основной сайт и меню могут оставаться на обычном хостинге, а WebSocket-сервер работает на небольшом VPS. Ниже минимальный сервер на Node.js с библиотекой ws: он рассылает каждый новый заказ всем подключённым экранам и убирает оборвавшиеся соединения.

// server.js  (npm install ws)
const { WebSocketServer } = require('ws');

const wss = new WebSocketServer({ host: '127.0.0.1', port: 8080 });

wss.on('connection', (ws) => {
  ws.isAlive = true;
  ws.on('pong', () => { ws.isAlive = true; });

  ws.on('message', (data) => {
    // разослать новый заказ всем подключённым экранам
    for (const client of wss.clients) {
      if (client.readyState === 1) client.send(data.toString());
    }
  });
});

// проверка «жив ли клиент» раз в 30 секунд
setInterval(() => {
  for (const ws of wss.clients) {
    if (!ws.isAlive) { ws.terminate(); continue; }
    ws.isAlive = false;
    ws.ping();
  }
}, 30000);

Обратите внимание: сервер слушает только адрес 127.0.0.1, то есть напрямую снаружи к нему не подключиться. Внешние соединения принимает Nginx, шифрует их и передаёт внутрь. Так и безопаснее, и сертификатом можно управлять в одном месте.

Настройка wss:// через Nginx

Если сайт открывается по HTTPS, браузер заблокирует незашифрованное соединение ws:// как смешанный контент, поэтому нужен wss://. Удобнее всего держать сертификат Let's Encrypt на Nginx, а запросы к адресу /ws/ проксировать на внутренний сервер Node.js. Ключевой момент — передать заголовки Upgrade и Connection и включить HTTP/1.1, иначе Nginx воспримет соединение как обычный запрос.

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

server {
    listen 443 ssl;
    server_name misol.uz;
    # ssl_certificate ... (сертификат Let's Encrypt)

    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_set_header Host $host;
        proxy_read_timeout 3600s;
    }
}

Важно и значение proxy_read_timeout. По умолчанию Nginx закрывает соединение, если 60 секунд не было данных. Пинг раз в 30 секунд в сервере выше создаёт трафик раньше этого срока, а увеличенный тайм-аут даёт дополнительный запас. В браузере соединение открывается так:

const sock = new WebSocket('wss://misol.uz/ws/');
sock.onmessage = (e) => showOrder(JSON.parse(e.data));
sock.onclose = () => setTimeout(() => location.reload(), 3000);

Проверка соединения и постоянная работа сервера

Проверить настройку можно ещё до браузера, с помощью curl. Если первая строка ответа — HTTP/1.1 101 Switching Protocols, то и Nginx, и Node.js работают правильно. Если пришёл 400 или 502, сначала убедитесь, что процесс Node.js запущен, а затем проверьте заголовки в конфигурации Nginx.

curl -i -N \
  -H "Connection: Upgrade" -H "Upgrade: websocket" \
  -H "Sec-WebSocket-Version: 13" \
  -H "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==" \
  https://misol.uz/ws/

Не оставляйте сервер Node.js запущенным вручную в терминале: при закрытии SSH-сессии он может остановиться. Оформите его как службу systemd, тогда он поднимется автоматически и после перезагрузки сервера, и после падения программы.

# /etc/systemd/system/ws-kitchen.service
[Service]
WorkingDirectory=/opt/ws-kitchen
ExecStart=/usr/bin/node server.js
Restart=always

[Install]
WantedBy=multi-user.target
sudo systemctl enable --now ws-kitchen
sudo nginx -t && sudo systemctl reload nginx

Несколько процессов и масштабирование

Одного процесса Node.js хватает для приложения со средней нагрузкой. Если позже вы запустите несколько процессов или серверов, клиент, подключённый к одному процессу, не увидит сообщение, пришедшее в другой. Тогда процессам нужно обмениваться сообщениями через общую очередь, и чаще всего эту роль выполняет механизм pub/sub в Redis. В небольшом проекте с этим не стоит спешить: сначала добейтесь стабильной работы одного процесса.

Итак, WebSocket требует постоянного процесса, открытого порта и доступа к настройкам веб-сервера — всё это есть на VPS. Заказав VPS в кабинете sayt.uz, можно оставить основной сайт на хостинге и вынести на отдельный сервер только часть, работающую в реальном времени.

Похожие статьи

📤 Не загружается большой файл: upload_max_filesize и post_max_size ⚡ Как измерить скорость сайта в Tas-IX: команды и разбор результатов 🐢 Лог медленных запросов MySQL: как найти проблемный запрос 📦 Переезд с Plesk на другую панель: что забрать и о чём не забыть
🌐 Язык
🇺🇿 O'zbek 🇺🇿 Ўзбек 🇷🇺 Русский ✓ 🇬🇧 English