Tutorials

MQTT im Browser mit reinem JavaScript (ohne Build-Schritt)

MQTT.js 5 per CDN in eine einzelne HTML-Datei laden und über sichere WebSockets verbinden: publishen, abonnieren, reconnecten und debuggen ohne npm oder Bundler.

Aktualisiert · 8 Min. Lesezeit

MQTT im Browser mit reinem JavaScript (ohne Build-Schritt)

Du brauchst weder npm noch React noch einen Bundler, um aus einer Webseite heraus MQTT zu sprechen. MQTT.js 5 bringt ein fertiges Browser-Bundle mit: ein <script>-Tag einfügen, mqtt.connect('wss://…') aufrufen, und die Seite publisht und abonniert über sichere WebSockets. Dieser Guide baut einen vollständigen Client in einer einzigen Datei, die du direkt von der Festplatte öffnen, auf jeden statischen Host legen oder in ein internes Tool einfügen kannst.

Warum Browser WebSockets brauchen

Browser können keine rohen TCP-Sockets öffnen, deshalb sind mqtt://host:1883 und mqtts://host:8883 für eine Webseite unerreichbar. Broker lösen das mit einem WebSocket-Listener: Dieselben MQTT-Pakete reisen in WebSocket-Frames. Für broker.connectmq.com sind die Endpoints:

URLEinsatz
ws://broker.connectmq.com:8083/mqttUnverschlüsselter WebSocket, nur für Seiten über http:// oder file://
wss://broker.connectmq.com:8084/mqttSicherer WebSocket (TLS), funktioniert von jeder Seite; standardmäßig verwenden

Der Pfad /mqtt ist wichtig: Wer den richtigen Port mit dem falschen Pfad anspricht, scheitert meist schon beim HTTP-Upgrade (siehe WebSocket-Pfad 404). MQTT über WebSockets erklärt den Transport im Detail.

Die komplette Seite

Speichere das als index.html. Die Seite pinnt eine exakte MQTT.js-Version vom CDN, die ein globales mqtt-Objekt bereitstellt.

index.html
<!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>

Öffne die Datei, tippe eine Nachricht und klick auf Publish: Die Seite abonniert ihr eigenes zufälliges Topic, also kommt die Nachricht über den Broker zurück und erscheint im Log. Jeder Tab bekommt ein eigenes Topic und eine eigene Client-ID, zwei offene Tabs stören sich also nicht. Um den Traffic von außen zu sehen, öffne den Online-MQTT-Client, abonniere testmqtt/browser-demo/# und publishe von einer der beiden Seiten.

ES-Modul-Variante

Wenn du Module bevorzugst: MQTT.js veröffentlicht auch einen ESM-Build. Es wird kein Global angelegt, du importierst ihn also:

Modul-Script
<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>

Modul-Scripts werden automatisch verzögert ausgeführt und legen keine Variablen in window ab. Manche Browser blockieren Modul-Scripts auf Seiten, die über file:// geöffnet wurden. Liefere den Ordner deshalb über einen lokalen Webserver aus:

Ordner lokal ausliefern
python3 -m http.server 8000
# dann http://localhost:8000/ öffnen

Optionen, die im Browser zählen

OptionEmpfehlungWarum
clientIdZufällig pro TabZwei Tabs mit derselben ID werfen sich gegenseitig in einer Schleife raus
protocolVersion5 (oder 4 für 3.1.1)MQTT 5 liefert Reason Codes in Fehlern, das erleichtert das Debugging
keepalive30–60 sLaptops gehen in den Ruhezustand und WLANs wechseln; ein kurzes Keep-Alive erkennt tote Sockets früher
reconnectPeriod2.000–5.000 ms0 schaltet Auto-Reconnect ab; sehr kleine Werte hämmern auf den Broker ein
connectTimeout10.000 msSchnell scheitern und dem Nutzer einen Status zeigen, statt endlos zu laden
cleantrueEin Browser-Tab braucht selten eine persistente Session

Erzeuge IDs mit einem Präfix, das du in den Broker-Logs wiedererkennst, oder nutze den Client-ID-Generator. Brauchst du dasselbe Setup in einer anderen Sprache, liefert der MQTT-Codegenerator entsprechende Snippets.

Zugangsdaten in einer statischen Seite

Alles in einer HTML-Datei ist öffentlich: Jeder kann sich den Quelltext ansehen und username und password kopieren. Behandle Browser-Zugangsdaten entsprechend:

  • Leg einen eigenen Broker-Benutzer für die Seite an und beschränke ihn per ACL auf genau die Topics, die sie braucht. Ein Dashboard braucht meist nur Lesezugriff auf .../telemetry/#.
  • Verwende im Browser niemals einen Geräte-, Backend- oder Admin-Account wieder.
  • Für eingeloggte Nutzer lässt du dein Backend nach der Authentifizierung benutzerbezogene oder kurzlebige Zugangsdaten ausstellen und übergibst sie an mqtt.connect.

MQTT-WebSocket-Authentifizierung im Browser geht diese Muster im Detail durch.

Die Verbindung debuggen

Öffne die DevTools (F12) und schau an zwei Stellen nach:

  1. Console. Hier erscheinen Fehler aus dem error-Event und die WebSocket-Meldungen des Browsers selbst.
  2. Network → WS. Wähl die Verbindung aus, um das HTTP-Upgrade (Status 101 Switching Protocols bei Erfolg) und die binären MQTT-Frames im Tab Messages zu sehen.
Meldung in der KonsoleWahrscheinliche UrsacheLösung
Mixed Content: The page at 'https://…' was loaded over HTTPS, but attempted to connect to the insecure WebSocket endpoint 'ws://…'ws:// auf einer HTTPS-Seitewss://…:8084/mqtt verwenden
WebSocket connection to 'wss://…' failedFalscher Port oder Pfad, Firewall, TLS-ProblemPort 8084 und Pfad /mqtt prüfen; siehe WebSocket-Verbindung schlägt fehl
connack timeoutWebSocket offen, aber kein CONNACK rechtzeitigBroker überlastet oder ein Proxy puffert; connectTimeout erhöhen und erneut versuchen
Connection refused: Not authorized / Bad User Name or PasswordFehlende oder falsche ZugangsdatenBenutzername, Passwort und die ACLs des Benutzers prüfen
Status springt zwischen verbunden und reconnectingDoppelte Client-ID in einem anderen Tab oder GerätclientId pro Tab eindeutig machen

Das Reason-Code-Nachschlagewerk übersetzt die numerischen Codes, die MQTT-5-Broker zurückgeben. Um deine Seite komplett auszuschließen, probier dieselbe URL im Online-Client: Verbindet er sich dort, liegt das Problem in deinem Code, nicht am Broker oder Netzwerk.

Topic der Demo-Seite im Online-Client ansehen

Wie es weitergeht

Die Seite oben ist ein vollständiger Client. Typische nächste Schritte sind eine Chat-Oberfläche (ein Topic pro Raum, Presence über Last Will), ein Live-Chart mit Sensor-Telemetrie oder ein Bedienpanel, das Befehle publisht. Wenn der Code über ein paar hundert Zeilen wächst, ist der Umstieg auf das npm-Paket und einen Bundler einfach: Die mqtt.connect-API ist identisch, weshalb der MQTT.js-Guide unverändert gilt.

Ein Script-Tag, eine wss://-URL und ein paar Event-Handler sind alles, was ein Browser für MQTT braucht. Pinne die Bibliotheksversion, gib jedem Tab eine eigene Client-ID, liefere nur einen eingeschränkten Account an den Browser aus und prüfe jede Änderung in den DevTools und im Online-Client, bevor du deployst.

Häufig gestellte Fragen

Kann sich ein Browser auf Port 1883 oder 8883 mit MQTT verbinden?

Nein. Browser können keine rohen TCP-Sockets öffnen, deshalb erreicht eine Webseite einen MQTT-Broker nur über einen WebSocket-Listener. Nutze wss:// (Port 8084 mit dem Pfad /mqtt bei broker.connectmq.com) von jeder per HTTPS ausgelieferten Seite.

Ist es sicher, MQTT-Zugangsdaten in eine statische HTML-Seite zu schreiben?

Alles in der Seite kann jeder Besucher lesen. Nutze einen eigenen Broker-Benutzer, dessen ACL nur die Topics erlaubt, die die Seite braucht (oft nur lesend), oder lass dein Backend kurzlebige Zugangsdaten ausgeben. Liefere niemals einen Admin- oder Geräte-Account an den Browser aus.

Warum funktioniert meine Seite lokal, aber nicht nach dem Deployment?

Meistens wegen Mixed Content. Eine über https:// ausgelieferte Seite darf keine unsichere ws://-Verbindung öffnen, also blockiert der Browser sie. Stell die URL auf wss:// am TLS-WebSocket-Port um und prüfe den Pfad.