Reference

MQTT vs HTTP for IoT: When to Use Each

Every IoT tutorial starts with HTTP. POST a JSON body to a webhook, you are connected. It works, it is well-documented, and it requires no infrastructure beyond your existing web stack. For most beginners, HTTP is the default and reasonably so.

The default stops being reasonable as soon as your device has more than one update per minute, runs on battery, or shares a network with hundreds of similar devices. Then MQTT — a protocol designed specifically for these constraints — becomes the right choice. This article is the side-by-side comparison.

How they actually look on the wire

sequenceDiagram participant D as Device participant B as Server / Broker Note over D,B: HTTP POST per reading D->>B: TCP SYN B-->>D: TCP SYN-ACK D->>B: TCP ACK D->>B: TLS handshake (4 round trips) D->>B: POST /ingest with JSON body B-->>D: 200 OK D->>B: TCP FIN (close) Note over D,B: MQTT subscribe-based D->>B: TCP + TLS (one time) D->>B: CONNECT B-->>D: CONNACK D->>B: SUBSCRIBE topic/cmd Note over D,B: Connection persists; many readings flow D->>B: PUBLISH topic/temp 22.5 D->>B: PUBLISH topic/temp 22.6 B-->>D: PUBLISH topic/cmd reboot

HTTP requires a fresh TCP connection per request. MQTT establishes one persistent connection and reuses it for every message in both directions.

The numbers that matter

For a battery sensor reporting one reading every minute over the day, here is what each protocol costs.

Per reading (with TLS):
                              HTTPS         MQTT
  Bytes on the wire           ~1500         ~50
  TCP/TLS handshake bytes     ~3000         ~3000 (once)
  Time on radio               ~3 seconds    ~0.3 seconds
  Energy per reading (ESP32)  ~0.05 mAh     ~0.005 mAh

Over 24 hours at 1 reading/minute:
  Total bytes                 ~2.1 MB       ~75 KB
  Total radio time            ~72 minutes   ~7 minutes
  Battery consumed             ~72 mAh       ~7 mAh

The 10× difference is not theoretical — it shows up directly in battery life. A device that runs three months on HTTP runs roughly two and a half years on MQTT, with no other changes.

Why MQTT is so much smaller

HTTP is a request-response protocol designed for human-readable text. Every request includes headers (User-Agent, Content-Type, Host, etc.) that add hundreds of bytes. The response includes status, more headers, and content. Most of the bytes are protocol overhead, not your data.

MQTT was designed in 1999 for SCADA systems running over satellite links. The header is two bytes. The topic name is shared between subscribe and publish messages, so it is only sent occasionally. The payload is a binary blob of whatever size you specify. Sending the number 22.5 takes 4 bytes plus 2 bytes of MQTT header plus 0 bytes of TLS overhead (the TLS session is amortised across thousands of messages).

The persistent connection model

The deeper architectural difference: MQTT keeps the TCP connection alive for hours or days. The device subscribes once at startup and stays connected. The broker can push messages to the device at any time without the device polling.

This enables three patterns that HTTP makes painful:

  • Server-pushed commands. The cloud sends "reboot" or "update firmware" to a specific device by publishing to its subscribed topic. With HTTP, the device must poll periodically to check.
  • Last-will messages. If the device disconnects unexpectedly, the broker publishes a pre-configured "offline" message on the device's behalf. The cloud knows immediately, without a separate heartbeat protocol.
  • Pub-sub fan-out. A single publish from one device reaches every subscriber to that topic. Useful for sensor data that multiple consumers want (a dashboard, an alerting service, a logger).

QoS levels

MQTT defines three quality-of-service levels:

  • QoS 0: at most once. Send and forget. Lost messages are gone. Use for high-frequency, low-importance data (continuous sensor readings).
  • QoS 1: at least once. Broker acknowledges receipt; sender retries until acknowledged. Possible duplicates. Use for most application messages.
  • QoS 2: exactly once. Four-way handshake to guarantee delivery without duplicates. Slow, complex; rarely worth it.

Pick QoS 0 by default. Step up to QoS 1 for messages where loss is unacceptable (commands, alerts). QoS 2 is almost never the right answer.

When HTTP wins

HTTP is the right choice when:

  • Reports are infrequent. One message every hour or less; the per-message overhead is irrelevant compared to the deep-sleep idle.
  • You do not control the backend. The webhook is what someone gives you. Adding an MQTT broker is your problem to solve.
  • Firewall constraints. Many corporate networks block port 1883 (MQTT) but allow 443 (HTTPS). Some MQTT brokers run over WebSockets on 443 to work around this.
  • One-shot communication. The device boots, sends one message, sleeps. No persistent connection needed.
  • Simplicity matters more than efficiency. A prototype that needs to ship next week.

When MQTT wins

MQTT is the right choice when:

  • Messages are frequent. Anything more than a few per minute. The per-message overhead of HTTP becomes painful.
  • Battery life matters. The 10× energy difference is decisive.
  • The device must respond to remote commands. Without polling.
  • Many devices share infrastructure. An MQTT broker handles thousands of devices on one machine. HTTP servers under similar load are dramatically harder.
  • Network is unreliable. MQTT's persistent connection with auto-reconnect handles flaky networks gracefully.

Code: same temperature reading, both ways

HTTP version:

void send_http(float temp) {
    HTTPClient http;
    http.begin("https://api.example.com/ingest");
    http.addHeader("Content-Type", "application/json");
    char body[64];
    snprintf(body, sizeof body, "{\"temp\":%.2f}", temp);
    http.POST(body);
    http.end();
}

MQTT version (using PubSubClient):

WiFiClient wifi;
PubSubClient mqtt(wifi);

void setup_mqtt() {
    mqtt.setServer("broker.example.com", 1883);
    mqtt.connect("sensor-01");
}

void send_mqtt(float temp) {
    if (!mqtt.connected()) mqtt.connect("sensor-01");
    char body[16];
    snprintf(body, sizeof body, "%.2f", temp);
    mqtt.publish("sensors/01/temp", body);
    mqtt.loop();
}

Both are about ten lines. The MQTT version is more efficient on every metric but requires a broker (Mosquitto self-hosted, or HiveMQ Cloud / EMQX Cloud / AWS IoT Core managed).

Frequently Asked Questions

What broker should I use?

For learning: Mosquitto, run locally with one apt-get install. For free hosted: HiveMQ Cloud (10 MB/month free) or EMQX Cloud Serverless. For a small fleet: a $5 VPS running Mosquitto. For thousands of devices: AWS IoT Core, Azure IoT Hub, or self-hosted EMQX cluster.

Is MQTT secure?

It can be. MQTT-over-TLS (port 8883) encrypts the connection. Authentication is via username/password, client certificates, or token-based methods like AWS IoT's Cognito integration. Avoid plain MQTT on port 1883 over public networks.

Can I use MQTT in a browser?

Yes, via MQTT-over-WebSockets. A web dashboard can subscribe to the same topics your devices publish to and update in real time. Eclipse Paho JavaScript client and MQTT.js both support this.

What about CoAP?

CoAP (Constrained Application Protocol) is an alternative for very low-power IoT, designed for sleepy devices and small payloads. Used in Thread and some industrial IoT. Less mainstream than MQTT but worth knowing exists.

Should my LoRa nodes use MQTT?

Not directly. LoRa's bandwidth is too constrained for MQTT framing overhead. The pattern: LoRa nodes use a custom binary packet format, the gateway translates to MQTT, MQTT carries the data the rest of the way.

Share your thoughts

Worked with this in production and have a story to share, or disagree with a tradeoff? Email us at support@mybytenest.com — we read everything.