ZIM-НИЙ SAAALEЗимние скидки: до −50% на старт и −20% на продление
до 31.01.2026 Подробнее
Выберите продукт

MQTT over WebSocket через Nginx: Mosquitto, TLS, keepalive и timeouts

Разбираем, как поднять Mosquitto и безопасно отдать его в браузер через Nginx (WSS). Дам рабочие конфиги брокера и reverse proxy, оптимальные keepalive/timeouts, фильтр Origin и лимиты, тесты mosquitto_pub/sub и Paho JS, отладку, производительность и чек‑лист перед продом.
MQTT over WebSocket через Nginx: Mosquitto, TLS, keepalive и timeouts

Когда MQTT выходит за пределы устройств и попадает в браузер, нам неизбежно нужен WebSocket. Браузеры не умеют в сырой TCP на порту 1883, зато отлично работают с ws:// и wss://. Самый простой и управляемый путь — поставить брокер Mosquitto за Nginx, а на периметре завершать TLS и делать nginx reverse proxy для WebSocket апгрейда. В этой инструкции соберём рабочую схему, разберём конфиг, таймауты, keepalive, тестирование и типичные грабли.

Зачем MQTT over WebSocket и почему через Nginx

MQTT поверх WebSocket обеспечивает доступ из веб-клиентов без лишних костылей. Дальше — вопрос удобства эксплуатации и безопасности. Завершая TLS на Nginx, мы получаем:

  • Единый TLS-терминатор и понятный контроль сертификатов, шифров и HSTS.
  • Гибкую маршрутизацию и защиту от всплесков на уровне HTTP (лимиты, логирование, доступ по Origin).
  • Удобные таймауты и читаемые логи, пригодные для централизованного сбора.
  • Простой путь к масштабированию за счёт upstream-групп или перенаправлений.

Брокер Mosquitto остаётся внутри, слушает websockets-листенер только на 127.0.0.1 (или в приватной сети), а наружу выходит только Nginx с wss://. Это и безопасно, и прозрачно. Разворачивать связку удобно на облачном VDS, особенно если нужна изоляция и контроль ресурсов.

Архитектура: потоки и порты

Базовая раскладка портов в типичной инсталляции:

  • Клиент → Nginx: 443/tcp (wss://, TLS завершаем в Nginx).
  • Nginx → Mosquitto: 9001/tcp (ws:// до локального websockets-листенера).
  • Локальные или приватные издатели/подписчики: по желанию 1883/tcp без TLS только внутри контура.

Не открывайте наружу порт Mosquitto 9001 (websockets) и, как правило, 1883 (raw MQTT). Для внешнего трафика достаточно wss:// на 443/TCP через Nginx.

Настройка Mosquitto и Nginx для WSS на экранах терминала

Настраиваем Mosquitto для WebSocket

Пример минимального /etc/mosquitto/mosquitto.conf для работы за Nginx. Включаем аутентификацию, локальный TCP и websockets-листенер, ограничим keepalive и размер сообщений.

# /etc/mosquitto/mosquitto.conf
per_listener_settings true

# Без анонимов
allow_anonymous false
password_file /etc/mosquitto/passwd

# Персистентность (по желанию)
persistence true
persistence_location /var/lib/mosquitto/

# Локальный raw MQTT (для внутренних агентов)
listener 1883 127.0.0.1

# WebSocket для Nginx
listener 9001 127.0.0.1
protocol websockets

# Полезные лимиты
max_keepalive 600
message_size_limit 1048576

# Логи
log_type error
log_type warning
log_type notice
log_timestamp true

Создаём парольный файл и пользователя:

sudo mosquitto_passwd -c /etc/mosquitto/passwd iot_admin
sudo systemctl restart mosquitto

Для узлов с большим числом одновременных соединений увеличьте лимиты файловых дескрипторов через drop-in для systemd:

sudo systemctl edit mosquitto
[Service]
LimitNOFILE=65536
Restart=always
RestartSec=2s
sudo systemctl daemon-reload
sudo systemctl restart mosquitto

Помните, что Mosquitto не перечитывает весь конфиг по SIGHUP; существенные изменения требуют рестарта службы.

Nginx: TLS и reverse proxy для WebSocket

Нам нужен корректный апгрейд до WebSocket (заголовки Upgrade и Connection), адекватные таймауты и выключенное буферизование. TLS завершаем на периметре, выставляем современные протоколы и защищённые настройки сессий. Для публичного доступа используйте доверенный сертификат из раздела SSL-сертификаты. Если параллельно настраиваете HSTS и перенос домена — пригодится разбор переезда домена с 301 и HSTS.

# В http{}.
map $http_upgrade $connection_upgrade {
    default upgrade
    ''      close
}

# Локальный upstream до Mosquitto WebSocket listener
upstream mosquitto_ws {
    server 127.0.0.1:9001 max_fails=2 fail_timeout=10s;
    keepalive 32;
}

# Необязательный фильтр по Origin (замените example.com на ваш домен)
map $http_origin $ws_allowed {
    default 0;
    ~^https?://(www\.)?example\.com$ 1;
}

# Лимит логирования только рукопожатий 101 (по желанию)
map $status $ws_handshake { ~^101$ 1; default 0; }
log_format mqtt '$remote_addr - $host [$time_local] "$request" $status $body_bytes_sent '
                '"$http_user_agent" "u=$http_upgrade" "c=$connection"';

server {
    listen 443 ssl http2;
    server_name mqtt.example.com;

    # TLS (заглушки путей под ваши сертификаты)
    ssl_certificate /etc/ssl/certs/fullchain.pem;
    ssl_certificate_key /etc/ssl/private/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256;
    ssl_prefer_server_ciphers off;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 1d;
    ssl_session_tickets off;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;

    access_log /var/log/nginx/mqtt_access.log mqtt if=$ws_handshake;
    error_log  /var/log/nginx/mqtt_error.log warn;

    # Точка подключения WebSocket клиентов
    location /mqtt {
        # Необязательная проверка Origin
        if ($ws_allowed = 0) { return 403; }

        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection $connection_upgrade;

        # Стандартные заголовки для обратного прокси
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;

        # Таймауты для длинных соединений
        proxy_connect_timeout 5s;
        proxy_read_timeout 1h;
        proxy_send_timeout 1h;

        # WS не буферизуем
        proxy_buffering off;

        # Не переключаем upstream при сбоях — WS плохо переживает
        proxy_next_upstream off;

        proxy_pass http://mosquitto_ws;
    }
}

Несколько замечаний:

  • proxy_read_timeout должен быть больше, чем MQTT keepalive клиента. Если клиенты посылают PINGREQ каждые 60 секунд, ставьте хотя бы 5–10 минут или больше под ваш сценарий.
  • Для WebSocket буферизация должна быть выключена: proxy_buffering off, иначе можно получить задержки и распухание памяти.
  • Журналируйте только рукопожатия 101 — объём логов меньше, а полезные параметры запроса остаются.
  • Наличие http2 на сервере не мешает WebSocket: браузеры инициируют WS по HTTP/1.1, а обычные запросы спокойно идут по HTTP/2.

Ограничения соединений и защита от всплесков

Во время массового переподключения клиентов (например, при кратковременном сетевом сбое) полезно ограничить скорость рукопожатий и коннектов на уровне Nginx. Пример базовых лимитов:

# В http{}
limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_req_zone  $binary_remote_addr zone=wsreq:10m rate=5r/s;

server {
    # ...
    location /mqtt {
        limit_conn perip 20;
        limit_req zone=wsreq burst=10 nodelay;
        # ... остальной конфиг из примера выше
    }
}

Значения подбирайте под поведение клиентов и ожидаемую нагрузку. Важен баланс: не «убить» легитимный шквал переподключений, но и не допустить лавинообразной нагрузки. Если нужен удобный GUI для сервера — пригодится сравнение панелей для VDS.

FastFox SSL
Надежные SSL-сертификаты
Мы предлагаем широкий спектр SSL-сертификатов от GlobalSign по самым низким ценам. Поможем с покупкой и установкой SSL бесплатно!

Keepalive и timeouts: практические пресеты

MQTT keepalive — это интервал, с которым клиент посылает PINGREQ, чтобы брокер (и все прокси) знали, что соединение живо. Если прокси не видит трафика дольше своего read_timeout, он закроет соединение.

  • Для браузеров и мобильных клиентов ставьте keepalive 30–60 секунд. В JS-клиентах это обычно keepAliveInterval.
  • В Nginx держите proxy_read_timeout минимум в 5–10 раз больше клиентского keepalive (например, 10–30 минут). Для нестабильных сетей иногда требуется 1–2 часа.
  • У Mosquitto лимитируйте max_keepalive (например, 600 секунд), чтобы плохо настроенные клиенты не висели без пингов.
  • TCP keepalive ядра (net.ipv4.tcp_keepalive_*) полезен для «застрявших» соединений на уровне NAT/фаерволов, но не заменяет MQTT keepalive.

Пример системных настроек TCP keepalive (опционально, для серверов с большим количеством долгоживущих соединений):

# /etc/sysctl.d/99-mqtt-ws.conf
net.ipv4.tcp_keepalive_time=600
net.ipv4.tcp_keepalive_intvl=30
net.ipv4.tcp_keepalive_probes=5

Применить:

sudo sysctl --system

Тесты mosquitto_pub/sub и браузерный клиент по WSS

Тестируем: CLI и браузер

Проверим, что проксирование wss:// работает. Начнём с CLI-утилит из пакета Mosquitto. Система должна доверять вашему сертификату (или укажите путь к корневым сертификатам).

# Подписка через WSS на 443
mosquitto_sub -h mqtt.example.com -p 443 --capath /etc/ssl/certs --protocol websockets -t 'test/hello' -v -u iot_admin -P 'secret'

# Публикация сообщения
mosquitto_pub -h mqtt.example.com -p 443 --capath /etc/ssl/certs --protocol websockets -t 'test/hello' -m 'hi from wss' -u iot_admin -P 'secret'

В браузере (например, Paho MQTT JS) это выглядит так:

// Пример инициализации Paho MQTT JS по WSS через Nginx
const clientId = "web_" + Math.random().toString(16).slice(2, 10);
const client = new Paho.MQTT.Client("mqtt.example.com", 443, "/mqtt", clientId);

client.onConnectionLost = (res) => console.warn("lost", res.errorMessage);
client.onMessageArrived = (msg) => console.log(msg.destinationName, msg.payloadString);

client.connect({
  useSSL: true,
  userName: "iot_admin",
  password: "secret",
  keepAliveInterval: 60,
  reconnect: true,
  cleanSession: true,
  onSuccess: () => {
    console.log("connected");
    client.subscribe("test/hello", { qos: 1 });
  },
  onFailure: (e) => console.error("connect failed", e)
});

Обратите внимание на путь /mqtt — он должен совпадать с локацией в конфиге Nginx. Mosquitto не использует путь при обработке WebSocket (для него это просто транспорт), но прокси — использует.

Отладка: что ломается чаще всего

  • 400/403 на рукопожатии. Проверьте заголовки Upgrade и Connection. В Nginx для WS обязательно нужны proxy_set_header Upgrade $http_upgrade и proxy_set_header Connection $connection_upgrade. Если включили фильтр по Origin — убедитесь, что он пропускает ваш сайт.
  • Обрыв через ровно N минут. Признак слишком малого proxy_read_timeout на Nginx или слишком большого MQTT keepalive у клиента. Уменьшите keepalive клиента и/или увеличьте read_timeout прокси.
  • "upstream prematurely closed connection" в error_log. Чаще всего брокер завершил соединение (лимиты, авторизация) или вы подключились не по WebSocket, а сырой MQTT на /mqtt. Проверьте протокол клиента и логи Mosquitto.
  • Проблемы с сертификатом. Для CLI укажите --capath /etc/ssl/certs или путь к CAfile, которым подписан серверный сертификат. В браузере используйте публично доверенный сертификат.
  • Зависания после апдейта. nginx -s reload безопасно перезагружает конфиг без разрыва активных WS, а Mosquitto для изменений конфига чаще требует restart — планируйте окна.

Производительность и масштабирование

Для десятков тысяч соединений на узел следите за тремя классами ограничений: файловые дескрипторы, сетевые очереди ядра и память на прокси/брокере.

  • nofile для Nginx и Mosquitto (см. LimitNOFILE выше). В Nginx проверьте worker_connections и итоговый предел worker_processes * worker_connections.
  • Очереди сокетов: net.core.somaxconn, net.ipv4.tcp_max_syn_backlog, если наблюдаете Listen queue overflow.
  • Логи: не логируйте каждый WS-трафик, только рукопожатия. Иначе диски и CPU уйдут в логирование.

Горизонтальное масштабирование по нескольким брокерам возможно, но требует «прилипчивости» соединений. В классическом Nginx HTTP upstream есть ip_hash, но за NAT он может работать плохо. Для MQTT часто используют маршрутизацию по ClientID на уровне приложения или брокер-кластеры с синхронизацией состояния. Если всё же балансируете через Nginx, оставляйте proxy_next_upstream off, иначе WS не переживёт переключение корректно.

FastFox VDS
Облачный VDS-сервер в России
Аренда виртуальных серверов с моментальным развертыванием инфраструктуры от 195₽ / мес

Чек-лист перед продом

  • Mosquitto: allow_anonymous false, заведены пользователи, ограничен max_keepalive, проверена message_size_limit, локальный websockets-листенер на 127.0.0.1:9001.
  • Nginx: корректные заголовки Upgrade/Connection, длинные proxy_read_timeout, proxy_buffering off, отключён proxy_next_upstream для WS.
  • TLS: валидный сертификат, включены TLSv1.2/1.3, HSTS, отключены session tickets, если нет ротации ключей.
  • Безопасность: опциональный фильтр по Origin, лимиты limit_req/limit_conn, закрыты наружу порты Mosquitto.
  • Тесты: mosquitto_sub и mosquitto_pub по wss://, браузерный клиент, проверка реконнекта при рестарте брокера.
  • Мониторинг: метрики по числу соединений и задержкам, алерты на рост 4xx/5xx в Nginx и ошибки аутентификации в Mosquitto.

Итоги

Схема «Nginx как обратный прокси + Mosquitto за ним» даёт предсказуемый и безопасный способ отдать MQTT в браузер через wss://. Небольшой, но важный набор настроек — апгрейд заголовков, длинные таймауты, отключённая буферизация, аккуратные лимиты и базовая защита на периметре — избавит вас от рваных сессий и лишней возни. Дальше остаётся работать с логикой приложения и обдуманно масштабировать брокер там, где это действительно нужно.

Поделиться статьей

Вам будет интересно

Staging для WordPress на VDS: безопасные обновления без даунтайма OpenAI Статья написана AI (GPT 5)

Staging для WordPress на VDS: безопасные обновления без даунтайма

Staging-окружение для WordPress на VDS позволяет обновлять плагины, тему и ядро без риска для боевого сайта и без даунтайма. Разбе ...
Rate limiting API на Redis и Lua: простой и быстрый лимитер для микросервисов OpenAI Статья написана AI (GPT 5)

Rate limiting API на Redis и Lua: простой и быстрый лимитер для микросервисов

Разберёмся, как реализовать надёжный rate limiting для API на базе Redis и Lua. Сравним популярные алгоритмы лимитирования, спроек ...
CI/CD артефакты в S3 Object Storage: как раздавать через Nginx с правильным Cache-Control OpenAI Статья написана AI (GPT 5)

CI/CD артефакты в S3 Object Storage: как раздавать через Nginx с правильным Cache-Control

Артефакты CI/CD всё чаще выносят в S3‑совместимый Object Storage: билды, фронтенд‑статику, отчеты, архивы. Чтобы избежать проблем ...