Guides

MQTT over WebSockets: Connecting from the Browser

MQTT over WebSockets explained: why browsers need it, ws vs wss, the /mqtt path, common ports, mixed content errors and a working browser example with MQTT.js.

Updated · 7 min read

MQTT normally runs over a raw TCP connection on port 1883 (or 8883 with TLS). Browsers cannot open raw TCP sockets, so a web app cannot speak MQTT that way. The solution is MQTT over WebSockets: the same MQTT packets, carried inside a WebSocket connection that every browser supports. This guide explains how it works and how to avoid the errors that trip most people up.

Why browsers need WebSockets

For security reasons, JavaScript in a web page can only make network connections through browser APIs — fetch, XMLHttpRequest, WebSocket, WebRTC and WebTransport. None of them can open an arbitrary TCP connection to port 1883. WebSockets are the natural fit for MQTT because they provide a long-lived, bidirectional, binary channel — exactly what MQTT needs.

Using WebSockets is not only for browsers. It also helps when:

  • a firewall or corporate proxy only allows web traffic on ports 80 and 443;
  • you want MQTT to share a load balancer or reverse proxy with your HTTP services;
  • you are building a dashboard, admin tool or mobile web app that shows live device data.

How MQTT over WebSockets works

  1. The client opens an HTTP(S) connection and sends a WebSocket upgrade request to a URL such as wss://broker.emqx.io:8084/mqtt, asking for the subprotocol mqtt.
  2. The broker answers 101 Switching Protocols and the connection becomes a WebSocket.
  3. From then on, MQTT packets (CONNECT, PUBLISH, …) travel inside binary WebSocket frames. Everything above the transport — QoS, retained messages, Last Will, MQTT 5 properties — works exactly as it does over TCP.

The broker must have a WebSocket listener enabled. That listener usually runs on a different port from the TCP listener, and some brokers expect a specific path.

One practical consequence: browsers do not let JavaScript set custom headers on a WebSocket request, so authentication normally happens inside MQTT itself, with the username and password fields of the CONNECT packet, rather than with HTTP headers or cookies.

ws vs wss

ws://wss://
EncryptionNone — credentials and payloads in clear textTLS, like HTTPS
Usable from HTTPS pagesNo (blocked as mixed content)Yes
Proxies and firewallsSometimes interfered withUsually passes on port 443
Typical useLocal development onlyEverything else

Because wss:// uses the browser's TLS stack, the broker's certificate must be trusted by the browser. A self-signed certificate will fail silently from JavaScript — open the https://host:port URL in a tab once to see the real certificate error.

Mixed content: the most common browser error

If your page is served over https:// (as almost every site is), the browser blocks any attempt to open a ws:// connection. The console shows a mixed content or security error and the MQTT client simply never connects. The fix is always the same: use the broker's wss:// endpoint. Our online MQTT client is served over HTTPS for this reason and only connects via wss://.

Ports and paths

There is no single standard WebSocket port for MQTT; each broker chooses its own. These are the endpoints of the public brokers in our broker directory:

Brokerws://wss://Path
broker.hivemq.com80008884/mqtt
broker.emqx.io80838084/mqtt
test.mosquitto.org80808081any
mqtt.eclipseprojects.io80443/mqtt

A wrong path typically produces a failed handshake (HTTP 404 or a closed connection) before MQTT even starts. A wrong port usually times out. For the full picture of TCP, TLS and WebSocket ports, see MQTT ports explained.

Example: MQTT in the browser with MQTT.js

MQTT.js is the most widely used JavaScript client and works in both Node.js and the browser. In the browser it automatically uses WebSockets:

browser.js
import mqtt from 'mqtt';

const client = mqtt.connect('wss://broker.emqx.io:8084/mqtt', {
  clientId: `web-${crypto.randomUUID().slice(0, 8)}`,
  // username: 'user', password: 'secret',  // for private brokers
  reconnectPeriod: 2000,
});

client.on('connect', () => {
  client.subscribe('testmqtt/ws-demo/#');
  client.publish('testmqtt/ws-demo/hello', 'hello from the browser');
});

client.on('message', (topic, payload) => {
  console.log(topic, payload.toString());
});

client.on('error', (err) => console.error('MQTT error:', err.message));

Enabling WebSockets on your own broker

If you run your own broker, the WebSocket listener is usually a few lines of configuration. In Mosquitto, for example, you add a second listener next to the TCP one:

mosquitto.conf
listener 1883
protocol mqtt

listener 8080
protocol websockets

For wss:// you can either configure certificates on that listener directly or, more commonly, terminate TLS at a reverse proxy on port 443 and forward plain WebSocket traffic to the broker. The proxy needs WebSocket support enabled:

nginx location block
location /mqtt {
    proxy_pass http://127.0.0.1:8080;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_read_timeout 1h;
}

Make sure the proxy read timeout is longer than your MQTT keep-alive interval; otherwise the proxy closes idle connections and clients see unexplained disconnects.

Troubleshooting checklist

  • Using port 1883 or 8883 from a browser? Those are TCP ports — switch to the WebSocket listener.
  • Page on HTTPS, URL starts with ws://? Mixed content — use wss://.
  • Handshake fails immediately? Check the path (/mqtt vs none) and the subprotocol.
  • Behind a reverse proxy? Nginx and similar proxies must forward the Upgrade and Connection headers and allow long-lived connections.
  • Connected but kicked out? That is an MQTT-level issue — see MQTT connection errors.

Test a wss:// connection now

Frequently asked questions

Why can my browser not connect to MQTT on port 1883?

Browsers cannot open raw TCP sockets, and port 1883 speaks plain MQTT over TCP. A browser client must connect to the broker’s WebSocket listener instead, using a ws:// or wss:// URL with the right port and path.

What is the difference between ws and wss for MQTT?

ws:// is an unencrypted WebSocket connection; wss:// is WebSocket over TLS. Pages served over HTTPS may only use wss://, and wss:// is the only sensible choice for anything beyond local testing.

What path should I use for MQTT over WebSockets?

It depends on the broker. Many brokers, including EMQX and HiveMQ, use /mqtt. Mosquitto accepts connections regardless of path. Check the broker documentation; a wrong path usually fails during the WebSocket handshake.