Você não precisa de npm, React nem bundler para falar MQTT em uma página web. O MQTT.js 5 já vem com um bundle pronto para o navegador: adicione uma tag <script>, chame mqtt.connect('wss://…') e a página publica e assina via WebSockets seguros. Este guia monta um cliente completo em um único arquivo, que você pode abrir direto do disco, subir em qualquer hospedagem estática ou colar em uma ferramenta interna.
Por que navegadores precisam de WebSockets
Navegadores não abrem sockets TCP diretos, então mqtt://host:1883 e mqtts://host:8883 estão fora do alcance de uma página web. Os brokers resolvem isso com um listener WebSocket: os mesmos pacotes MQTT trafegam dentro de frames WebSocket. Para broker.connectmq.com os endpoints são:
| URL | Uso |
|---|---|
ws://broker.connectmq.com:8083/mqtt | WebSocket sem criptografia, só para páginas servidas por http:// ou file:// |
wss://broker.connectmq.com:8084/mqtt | WebSocket seguro (TLS), funciona em qualquer página; use este por padrão |
O caminho /mqtt faz diferença: conectar na porta certa com o caminho errado costuma falhar durante o upgrade HTTP (veja erro 404 no caminho do WebSocket). MQTT sobre WebSockets explica o transporte em detalhes.
A página completa
Salve isto como index.html. Ele fixa uma versão exata do MQTT.js no CDN, que expõe um objeto global mqtt.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>MQTT browser client</title>
<script src="https://unpkg.com/mqtt@5.10.1/dist/mqtt.min.js"></script>
<style>
body { font: 15px/1.5 system-ui, sans-serif; max-width: 640px; margin: 2rem auto; padding: 0 1rem; }
#log { background: #111; color: #9f9; padding: .75rem; height: 260px; overflow: auto; font-family: monospace; }
input, button { font: inherit; padding: .4rem .6rem; }
</style>
</head>
<body>
<h1>MQTT over WSS</h1>
<p>Status: <strong id="status">connecting…</strong></p>
<form id="pub">
<input id="msg" placeholder="Message" required>
<button>Publish</button>
</form>
<pre id="log"></pre>
<script>
const URL = 'wss://broker.connectmq.com:8084/mqtt';
const TOPIC = 'testmqtt/browser-demo/' + Math.random().toString(16).slice(2, 8);
const $ = (id) => document.getElementById(id);
const log = (line) => {
$('log').textContent += new Date().toLocaleTimeString() + ' ' + line + '\n';
$('log').scrollTop = $('log').scrollHeight;
};
const client = mqtt.connect(URL, {
clientId: 'web-' + Math.random().toString(16).slice(2, 10),
// username: 'username', password: 'password', // see "Credentials" below
protocolVersion: 5,
clean: true,
keepalive: 30,
connectTimeout: 10_000,
reconnectPeriod: 2_000,
});
client.on('connect', () => {
$('status').textContent = 'connected';
client.subscribe(TOPIC, { qos: 1 }, (err) => {
log(err ? 'subscribe failed: ' + err.message : 'subscribed to ' + TOPIC);
});
});
client.on('message', (topic, payload) => {
log(topic + ' ' + payload.toString());
});
client.on('reconnect', () => { $('status').textContent = 'reconnecting…'; });
client.on('offline', () => { $('status').textContent = 'offline'; });
client.on('error', (err) => log('error: ' + err.message));
$('pub').addEventListener('submit', (e) => {
e.preventDefault();
client.publish(TOPIC, $('msg').value, { qos: 1 });
$('msg').value = '';
});
window.addEventListener('pagehide', () => client.end());
</script>
</body>
</html>Abra o arquivo, digite uma mensagem e clique em Publish: a página assina o próprio topic aleatório, então a mensagem volta pelo broker e aparece no log. Cada aba ganha seu próprio topic e client ID, então duas abas abertas não interferem uma na outra. Para ver o tráfego de fora, abra o cliente MQTT online, assine testmqtt/browser-demo/# e publique de qualquer um dos lados.
Variante com módulos ES
Se você prefere módulos, o MQTT.js também publica um build ESM. Nenhuma variável global é criada, então você o importa:
<script type="module">
import mqtt from 'https://unpkg.com/mqtt@5.10.1/dist/mqtt.esm.js';
const client = mqtt.connect('wss://broker.connectmq.com:8084/mqtt', { protocolVersion: 5 });
client.on('connect', () => client.publish('testmqtt/browser-demo/esm', 'hello from a module'));
</script>Scripts de módulo são adiados automaticamente e não vazam variáveis para window. Alguns navegadores bloqueiam scripts de módulo em páginas abertas via file://, então sirva a pasta com um servidor web local:
python3 -m http.server 8000
# depois abra http://localhost:8000/Opções que importam no navegador
| Opção | Recomendado | Por quê |
|---|---|---|
clientId | Aleatório por aba | Duas abas com o mesmo ID derrubam uma à outra em loop |
protocolVersion | 5 (ou 4 para 3.1.1) | O MQTT 5 traz reason codes nos erros, o que facilita a depuração |
keepalive | 30–60 s | Notebooks entram em suspensão e o Wi-Fi muda; um keep-alive curto detecta sockets mortos mais cedo |
reconnectPeriod | 2.000–5.000 ms | 0 desativa a reconexão automática; valores muito pequenos sobrecarregam o broker |
connectTimeout | 10.000 ms | Falha rápido e mostra um estado ao usuário em vez de ficar carregando para sempre |
clean | true | Uma aba do navegador raramente precisa de sessão persistente |
Gere IDs com um prefixo que você reconheça nos logs do broker, ou use o gerador de client ID. Se precisar da mesma configuração em outra linguagem, o gerador de código MQTT produz trechos equivalentes.
Credenciais em uma página estática
Tudo em um arquivo HTML é público: qualquer pessoa pode ver o código-fonte e copiar username e password. Trate as credenciais do navegador de acordo:
- Crie um usuário de broker dedicado à página e restrinja-o com ACLs exatamente aos topics de que ela precisa. Um dashboard geralmente só precisa de leitura em
.../telemetry/#. - Nunca reutilize no navegador uma conta de dispositivo, de backend ou de administrador.
- Para usuários logados, deixe o backend emitir credenciais por usuário ou de curta duração após a autenticação e passe-as para
mqtt.connect.
Autenticação MQTT via WebSocket no navegador detalha esses padrões.
Depurando a conexão
Abra as DevTools (F12) e confira dois lugares:
- Console. Aqui aparecem os erros do evento
errore as mensagens de WebSocket do próprio navegador. - Network → WS. Selecione a conexão para ver o upgrade HTTP (status
101 Switching Protocolsquando dá certo) e os frames MQTT binários na aba Messages.
| Mensagem no console | Causa provável | Correção |
|---|---|---|
Mixed Content: The page at 'https://…' was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint 'ws://…' | ws:// usado em uma página HTTPS | Use wss://…:8084/mqtt |
WebSocket connection to 'wss://…' failed | Porta ou caminho errados, firewall, problema de TLS | Confira a porta 8084 e o caminho /mqtt; veja falha na conexão WebSocket |
connack timeout | O WebSocket abriu, mas o CONNACK não chegou a tempo | Broker sobrecarregado ou um proxy segurando os dados; aumente connectTimeout e tente de novo |
Connection refused: Not authorized / Bad User Name or Password | Credenciais ausentes ou erradas | Confira usuário, senha e as ACLs do usuário |
| O status alterna entre conectado e reconectando | Client ID duplicado em outra aba ou dispositivo | Torne o clientId único por aba |
A consulta de reason codes traduz os códigos numéricos que brokers MQTT 5 retornam. Para descartar de vez a sua página, teste a mesma URL no cliente online: se conectar lá, o problema está no seu código, não no broker nem na rede.
Veja o topic da página demo no cliente online
Próximos passos
A página acima é um cliente completo. Os próximos passos típicos são uma interface de chat (um topic por sala, presença com Last Will), um gráfico ao vivo alimentado por telemetria de sensores ou um painel de controle que publica comandos. Quando o código passar de algumas centenas de linhas, migrar para o pacote npm e um bundler é fácil: a API mqtt.connect é idêntica, e é por isso que o guia do MQTT.js vale sem alterações.
Uma tag script, uma URL wss:// e alguns handlers de eventos são tudo de que um navegador precisa para falar MQTT. Fixe a versão da biblioteca, dê a cada aba seu próprio client ID, envie ao navegador apenas uma conta restrita e verifique cada mudança nas DevTools e no cliente online antes do deploy.
Perguntas frequentes
Um navegador consegue se conectar ao MQTT nas portas 1883 ou 8883?
Não. Navegadores não abrem sockets TCP diretos, então uma página web só alcança um broker MQTT por meio de um listener WebSocket. Use wss:// (porta 8084 com o caminho /mqtt em broker.connectmq.com) em qualquer página servida por HTTPS.
É seguro colocar credenciais MQTT em uma página HTML estática?
Tudo o que está na página pode ser lido por qualquer visitante. Use um usuário de broker dedicado cuja ACL só permita os topics de que a página precisa (muitas vezes só leitura), ou faça seu backend entregar credenciais de curta duração. Nunca envie ao navegador uma conta de administrador ou de dispositivo.
Por que minha página funciona localmente mas falha depois do deploy?
Geralmente por conteúdo misto. Uma página servida por https:// não pode abrir uma conexão ws:// insegura, então o navegador a bloqueia. Troque a URL para wss:// na porta WebSocket com TLS e confira o caminho.


