Tutoriels IoT

MQTT dans le navigateur avec JavaScript : un client minimal en une seule page HTML

Construisez une page HTML unique qui se connecte à broker.connectmq.com en WebSocket sécurisé, s'abonne, publie et affiche les messages en direct avec MQTT.js, sans étape de build ni backend.

Fenêtre de navigateur exécutant un client MQTT JavaScript qui publie et s'abonne en WebSocket sécurisé
Essayez-le sur votre propre broker MQTTObtenez un broker MQTT privé et managé avec l’offre Free et partez d’exemples de code prêts à l’emploi.
Essayer gratuitement Exemples de code Aucune carte bancaire requise

Faire tourner MQTT dans le navigateur avec JavaScript est le moyen le plus rapide de créer un tableau de bord en direct, un panneau de commande à distance ou un outil de débogage sans écrire de backend. Dans ce tutoriel, vous allez créer une page HTML autonome qui se connecte au serveur MQTT public gratuit broker.connectmq.com en WebSocket sécurisé, s'abonne à un topic, publie des messages et les affiche en temps réel. Vous verrez aussi pourquoi les navigateurs ont besoin du WebSocket pour MQTT, pourquoi CORS ne pose pas de problème et comment migrer la même page vers un broker privé en toute sécurité.

Pourquoi un navigateur a besoin de MQTT over WebSocket

Une page web ne peut pas ouvrir de sockets TCP bruts. Le bac à sable du navigateur ne laisse JavaScript accéder au réseau que via HTTP(S), WebSocket, WebRTC et quelques API plus récentes. Le MQTT standard fonctionne sur TCP, port 1883 (ou TLS sur le port 8883) : un navigateur ne peut donc pas atteindre ces ports directement.

La spécification MQTT résout ce problème en autorisant le transport des paquets MQTT dans des trames binaires WebSocket. Le broker expose un listener supplémentaire, le navigateur ouvre un WebSocket vers celui-ci, et à partir de là l'échange est du MQTT ordinaire : CONNECT, SUBSCRIBE, PUBLISH, PINGREQ. Si vous voulez le détail au niveau du protocole, notre guide MQTT over WebSocket décrit le handshake et l'en-tête de sous-protocole.

ConnectMQ expose deux listeners WebSocket sur chaque broker :

Endpoint URL Quand l'utiliser
WebSocket ws://broker.connectmq.com:8083/mqtt Tests locaux depuis une page http:// uniquement
WebSocket sécurisé wss://broker.connectmq.com:8084/mqtt Toute page servie en https://, et tout usage en production

Utilisez wss:// par défaut. Une page chargée en HTTPS n'a pas le droit d'ouvrir une connexion ws:// non chiffrée ; le navigateur la bloque en tant que contenu mixte avant même qu'un paquet ne quitte la machine.

Et CORS dans tout ça ?

CORS (Cross-Origin Resource Sharing) s'applique à fetch et à XMLHttpRequest. Les connexions WebSocket ne sont pas soumises aux requêtes preflight CORS. Le navigateur envoie un en-tête Origin pendant l'upgrade, et un serveur peut choisir de l'examiner, mais il n'y a pas de jeu avec Access-Control-Allow-Origin. C'est pourquoi vous pouvez ouvrir le fichier HTML ci-dessous depuis votre bureau, depuis un serveur de développement local ou depuis n'importe quel domaine, et il se connecte sans astuce de proxy.

Choisir une bibliothèque : MQTT.js

MQTT.js est le client MQTT de facto pour JavaScript. Le même package fonctionne dans Node.js et dans les navigateurs, prend en charge MQTT 3.1.1 et MQTT 5, et gère pour vous les reconnexions, le keepalive et les échanges QoS 1 et 2. La version 5 fournit un bundle navigateur précompilé : vous pouvez donc le charger depuis un CDN avec une seule balise script. La documentation de MQTT.js sur GitHub liste toutes les options ; le sous-ensemble ci-dessous suffit à un client minimal.

Si vous passez plus tard à un bundler ou à un framework, le code change à peine. Notre guide MQTT.js avec Node.js couvre le côté serveur, et le tutoriel React MQTT montre comment envelopper le même client dans des hooks.

Le client MQTT JavaScript complet pour navigateur, en une page

Enregistrez le contenu suivant sous mqtt-client.html et ouvrez-le dans n'importe quel navigateur moderne. Il charge MQTT.js depuis un CDN, génère un client ID unique et un préfixe de topic aléatoire, et vous propose des boutons pour vous connecter, vous abonner et publier.

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Minimal MQTT Browser Client</title>
  <script src="https://unpkg.com/mqtt@5.10.1/dist/mqtt.min.js"></script>
  <style>
    body { font-family: system-ui, sans-serif; max-width: 720px; margin: 2rem auto; padding: 0 1rem; }
    input, button { font: inherit; padding: .4rem .6rem; }
    #log { border: 1px solid #ccc; height: 280px; overflow-y: auto; padding: .5rem; font-family: monospace; font-size: .85rem; }
    .row { display: flex; gap: .5rem; margin: .5rem 0; }
    .row input { flex: 1; }
  </style>
</head>
<body>
  <h1>MQTT in the Browser</h1>
  <p>Status: <strong id="status">disconnected</strong></p>

  <div class="row">
    <input id="url" value="wss://broker.connectmq.com:8084/mqtt">
    <button id="connectBtn">Connect</button>
    <button id="disconnectBtn">Disconnect</button>
  </div>

  <div class="row">
    <input id="subTopic">
    <button id="subBtn">Subscribe</button>
  </div>

  <div class="row">
    <input id="pubTopic">
    <input id="pubPayload" value='{"temperature": 21.5}'>
    <button id="pubBtn">Publish</button>
  </div>

  <div id="log"></div>

  <script>
    const randomId = Math.random().toString(16).slice(2, 10);
    const prefix = `connectmq/demo/${randomId}`;
    document.getElementById("subTopic").value = `${prefix}/#`;
    document.getElementById("pubTopic").value = `${prefix}/sensors/room1`;

    const statusEl = document.getElementById("status");
    const logEl = document.getElementById("log");
    let client = null;

    function log(text) {
      const line = document.createElement("div");
      line.textContent = `[${new Date().toLocaleTimeString()}] ${text}`;
      logEl.appendChild(line);
      logEl.scrollTop = logEl.scrollHeight;
    }

    document.getElementById("connectBtn").onclick = () => {
      if (client) return;
      const url = document.getElementById("url").value;
      client = mqtt.connect(url, {
        clientId: `browser-${randomId}`,
        protocolVersion: 5,
        clean: true,
        keepalive: 30,
        connectTimeout: 10000,
        reconnectPeriod: 3000
        // Pour un broker ConnectMQ privé, ajoutez :
        // username: "YOUR_USERNAME",
        // password: "YOUR_PASSWORD"
      });

      client.on("connect", () => { statusEl.textContent = "connected"; log(`Connected to ${url}`); });
      client.on("reconnect", () => { statusEl.textContent = "reconnecting"; });
      client.on("close", () => { statusEl.textContent = "disconnected"; });
      client.on("offline", () => log("Client went offline"));
      client.on("error", (err) => log(`Error: ${err.message}`));
      client.on("message", (topic, payload, packet) => {
        const retained = packet.retain ? " (retained)" : "";
        log(`${topic}${retained}: ${payload.toString()}`);
      });
    };

    document.getElementById("disconnectBtn").onclick = () => {
      if (!client) return;
      client.end(false, () => log("Disconnected cleanly"));
      client = null;
    };

    document.getElementById("subBtn").onclick = () => {
      if (!client) return log("Connect first");
      const topic = document.getElementById("subTopic").value;
      client.subscribe(topic, { qos: 1 }, (err, granted) => {
        if (err) return log(`Subscribe failed: ${err.message}`);
        granted.forEach(g => log(`Subscribed to ${g.topic} (granted QoS ${g.qos})`));
      });
    };

    document.getElementById("pubBtn").onclick = () => {
      if (!client) return log("Connect first");
      const topic = document.getElementById("pubTopic").value;
      const payload = document.getElementById("pubPayload").value;
      client.publish(topic, payload, { qos: 1 }, (err) => {
        if (err) log(`Publish failed: ${err.message}`);
      });
    };
  </script>
</body>
</html>

Cliquez sur Connect, puis sur Subscribe, puis sur Publish. Le message part de votre page vers le broker et revient directement sur la même page, car vous êtes abonné à prefix/#, qui correspond au topic de publication.

Comment fonctionne le code

La connexion

mqtt.connect() renvoie immédiatement un client et se connecte en arrière-plan. Les événements vous indiquent la suite : connect après un CONNACK réussi, reconnect à chaque nouvelle tentative, close à la fermeture du socket et error pour des problèmes comme un login refusé. Définir protocolVersion: 5 vous donne accès aux fonctionnalités de MQTT 5 comme les reason codes et les user properties ; supprimez-le pour revenir à 3.1.1.

Le client ID est généré à chaque chargement de la page. Le serveur MQTT public gratuit accepte n'importe quel client ID non vide, mais deux clients avec le même ID s'éjectent mutuellement en boucle, ce qui ressemble à une connexion instable. Des ID aléatoires évitent ce problème quand vous ouvrez la page dans deux onglets.

L'abonnement et la réception

L'événement message se déclenche pour chaque PUBLISH entrant. payload est un buffer d'octets : appelez toString() pour du texte ou JSON.parse(payload.toString()) pour du JSON. Le troisième argument est le paquet brut, qui expose retain, qos et, en MQTT 5, properties.

Remarquez l'utilisation de textContent dans la fonction log. N'importe qui peut publier dans l'espace public du broker : traitez donc chaque payload comme une entrée non fiable. L'insérer avec innerHTML permettrait à un message malveillant d'exécuter du script dans votre page.

La publication

client.publish(topic, payload, options, callback) met le message en file d'attente. En QoS 1, le callback s'exécute après que le broker en a accusé réception par un PUBACK. Si la connexion est coupée, MQTT.js conserve les messages QoS 1 et 2 dans son stockage sortant et les envoie après la reconnexion, tant que la page reste ouverte. Le guide des niveaux de QoS explique quand cette garantie compte.

Tester depuis l'autre côté

Pour prouver que les messages passent réellement par le broker, observez-les depuis un terminal pendant que vous cliquez sur Publish dans la page. Remplacez le préfixe par celui qu'affiche votre page :

mosquitto_sub -h broker.connectmq.com -p 1883 -i cli-watch-7f3a -t 'connectmq/demo/YOUR_PREFIX/#' -v

Publiez depuis le terminal et le journal du navigateur se met à jour instantanément :

mosquitto_pub -h broker.connectmq.com -p 1883 -i cli-pub-7f3a -t 'connectmq/demo/YOUR_PREFIX/sensors/room1' -m '{"temperature": 23.1}'

Cet aller-retour, du navigateur vers un client TCP et inversement, est la démonstration la plus claire qu'un client WebSocket et un client TCP natif partagent le même espace de topics sur le broker.

Liste de vérification pour le dépannage

Quand le statut n'atteint jamais « connected », parcourez cette liste dans l'ordre :

  1. Mauvais chemin. ConnectMQ sert le WebSocket sur /mqtt. Une URL sans ce chemin échoue pendant l'upgrade HTTP.
  2. Contenu mixte. Une page https:// ne peut pas ouvrir de ws://. Utilisez wss:// sur le port 8084.
  3. Port bloqué. Certains réseaux d'entreprise n'autorisent que le port 443. Testez depuis un autre réseau ou un partage de connexion mobile pour écarter cette hypothèse.
  4. Client ID en double. Si le journal montre des connexions et fermetures qui se répètent toutes les quelques secondes, un autre client utilise le même ID.
  5. Identifiants vides ou invalides. Sur un broker privé, un nom d'utilisateur ou un mot de passe erroné produit un événement error avec « Not authorized » ou « Bad username or password ». Vérifiez les valeurs dans le portail.
  6. Limites du serveur MQTT public gratuit. Il autorise 10 messages par seconde et par client, 10 abonnements par client, 64 Ko par message et 8 niveaux de topic. Un bouton relié à une boucle rapide atteindra la limite de débit.
  7. DevTools. Ouvrez l'onglet Réseau, filtrez sur WS et inspectez les trames. Vous verrez les paquets MQTT binaires et le code de réponse de l'upgrade.

Passer du serveur MQTT public gratuit à un broker privé

Le serveur MQTT public gratuit est parfait pour ce tutoriel, mais chaque topic de son espace public est lisible par quiconque le devine. Pour de vrais projets, créez votre propre broker :

  1. Inscrivez-vous sur app.connectmq.com et choisissez l'offre Serverless gratuite.
  2. Créez un broker et copiez le nom d'utilisateur, le mot de passe et le client ID générés.
  3. Remplacez l'URL par l'endpoint WebSocket sécurisé de votre broker, sur le port 8084 avec le chemin /mqtt, et renseignez username, password et clientId dans les options.

Faites attention à l'endroit où ces identifiants sont stockés. Tout ce que vous placez dans du JavaScript côté navigateur peut être lu par quiconque ouvre les DevTools. Les bonnes pratiques sont les suivantes :

  • Utilisez des identifiants distincts et peu privilégiés pour les tableaux de bord dans le navigateur, jamais ceux de vos appareils.
  • Demandez à l'utilisateur de saisir les identifiants à l'exécution au lieu de les coder en dur dans la page.
  • Pour les sites publics, laissez un backend effectuer les publications privilégiées et gardez le navigateur en lecture seule. L'article sur les patterns MQTT avec Next.js montre cette séparation.

Le guide des bonnes pratiques de sécurité MQTT approfondit la gestion des identifiants et la conception des topics.

Pour aller plus loin

Cette page unique est une base que vous pouvez faire évoluer dans plusieurs directions :

  • Graphiques en direct. Parsez les payloads JSON et envoyez les valeurs dans une bibliothèque de graphiques. Conservez un tableau de longueur fixe pour que la mémoire reste stable lors des longues sessions.
  • Chat et présence. Les retained messages et le Last Will permettent d'afficher facilement qui est en ligne. Le tutoriel d'application de chat MQTT construit exactement cela.
  • Code typé. Si la page grossit, passez à TypeScript et à un bundler ; le guide MQTT avec TypeScript couvre le typage des payloads et des événements.
  • Autres langages. La vue d'ensemble des bibliothèques clientes MQTT compare MQTT.js aux clients Python, Go, Java et C# si votre backend utilise une autre stack.

Démarrer avec ConnectMQ

ConnectMQ vous offre un broker MQTT managé complet avec le WebSocket sécurisé déjà configuré sur le port 8084, un certificat Let's Encrypt, ainsi qu'un client MQTT intégré au navigateur et un explorateur de topics en direct dans le portail pour vérifier ce que publie votre page. Commencez dès aujourd'hui sur le serveur MQTT public gratuit, puis créez un broker MQTT cloud privé quand vous êtes prêt. Créez un compte gratuit avec l'offre Serverless, sans carte bancaire.

Questions fréquentes

Un navigateur peut-il se connecter à MQTT sur le port 1883 ?

Non. Les navigateurs ne peuvent pas ouvrir de connexions TCP brutes : ils doivent donc utiliser MQTT over WebSocket. Sur ConnectMQ, il s'agit du port 8083 pour ws:// et du port 8084 pour wss://, tous deux sur le chemin /mqtt.

Faut-il un backend ou un proxy pour utiliser MQTT dans le navigateur ?

Non. La page se connecte directement au broker en WebSocket, et le WebSocket n'est pas soumis aux requêtes preflight CORS. Un backend n'est utile que si vous voulez tenir les identifiants privilégiés à l'écart du client.

MQTT.js est-elle la seule bibliothèque MQTT JavaScript pour les navigateurs ?

C'est l'option la plus utilisée et la plus activement maintenue, avec la prise en charge de MQTT 5 et un bundle navigateur prêt à l'emploi. L'ancien client JavaScript Paho fonctionne toujours, mais il n'est plus le choix courant pour les nouveaux projets.

Pourquoi mon client navigateur se reconnecte-t-il toutes les quelques secondes ?

La cause la plus fréquente est que deux clients partagent le même client ID, ce qui amène le broker à déconnecter le plus ancien à chaque fois. Générez un ID aléatoire par onglet, et vérifiez aussi que vous ne dépassez pas les limites de débit du serveur MQTT public gratuit.

Articles liés

Les meilleurs outils CLI MQTT pour tester, scripter et déboguer

Un comparatif pratique de mosquitto_pub/sub, MQTTX CLI, HiveMQ MQTT CLI, la CLI MQTT.js, curl et mqttui, avec le même test exécuté sur le serveur MQTT public gratuit broker.connectmq.com pour choisir le bon outil pour le scripting et le débogage.

9 min de lecture

Tous les articles →

Votre broker MQTT privé, à quelques clics

Créez un compte, récupérez les identifiants de votre broker et connectez votre premier appareil — aucun serveur à installer, mettre à jour ou dimensionner.