Back to Blog
IoT Engineering

MQTT vs HTTP for IoT: Which Protocol Wins in Production?

Choosing between MQTT and HTTP can make or break an IoT deployment — the wrong choice adds latency, drains batteries, and balloons cloud costs. This guide gives engineers a clear decision framework backed by real benchmarks.

January 22, 2024
11 min read
MQTTHTTPIoT ProtocolsQoS

MQTT vs HTTP for IoT: Which Protocol Wins in Production?

Picking a communication protocol for an IoT system feels like it should be straightforward. In practice, the wrong choice costs you battery life, data reliability, and real money on cloud bills. At CodeCaracal we've shipped systems using both — and sometimes both at once. Here's the honest comparison.

Why the Protocol Choice Matters More Than You Think

A microcontroller waking up, sending a reading, and going back to deep sleep is a completely different workload from a streaming sensor publishing 10 readings per second. No single protocol fits both. The decision cascades into hardware selection, cloud architecture, and mobile app design.

Connection Overhead: Where HTTP Hurts the Most

Every HTTP request requires a full TCP handshake (or TLS handshake for HTTPS, which adds two more round trips). On a cellular NB-IoT link with 500 ms RTT, that's over 1.5 seconds of overhead before your 50-byte payload even leaves the device.

MQTT is a persistent connection protocol. The broker handshake happens once at startup. After that, publishing a telemetry message costs as little as 2 bytes of fixed header plus your payload. No headers, no method lines, no status codes.

HTTP POST (TLS, minimal headers) ≈ 800–2000 bytes per request
MQTT PUBLISH (QoS 0)            ≈  4–10 bytes overhead per message

For a device sending one reading per minute, HTTP is perfectly fine. For a device sending 10 readings per second, MQTT saves ~95% of bandwidth.

QoS: MQTT's Built-In Reliability Ladder

HTTP has no native retry semantics — your application code must handle failures. MQTT bakes three Quality-of-Service levels into the protocol:

| Level | Name | Guarantee | |-------|------|-----------| | QoS 0 | Fire-and-forget | Message may be lost | | QoS 1 | At-least-once | Delivered ≥1 times, may duplicate | | QoS 2 | Exactly-once | Delivered exactly once |

For sensor telemetry where losing an occasional reading is acceptable, QoS 0 is ideal — zero acknowledgement overhead. For actuator commands (open valve, trigger alarm), QoS 1 ensures the command lands. For billing or metering data, QoS 2 prevents double-counting at the cost of a four-message handshake.

See the deep-dive in [MQTT QoS 0, 1, and 2 Explained](/blog/mqtt-qos-levels-explained) for full protocol diagrams.

The Pub/Sub Model vs Request/Response

HTTP is fundamentally request/response. Every reading requires the device to initiate a connection. This breaks down immediately when you need to send a command *to* a device — you need long-polling, server-sent events, or WebSockets on top of HTTP to achieve push.

MQTT's pub/sub model solves this naturally. The device subscribes to devices/{deviceId}/commands at startup. The cloud publishes to that topic whenever it needs to send a command. The device receives it within milliseconds, with no polling loop required.

// Node.js: send a command to a specific device
const mqtt = require('mqtt')
const client = mqtt.connect('mqtts://your-broker:8883', {
  cert: fs.readFileSync('client.crt'),
  key: fs.readFileSync('client.key'),
  ca: fs.readFileSync('ca.crt'),
})

function sendCommand(deviceId, command) { const topic = devices/${deviceId}/commands const payload = JSON.stringify({ action: command, timestamp: Date.now(), correlationId: crypto.randomUUID(), }) client.publish(topic, payload, { qos: 1 }, (err) => { if (err) console.error('Command delivery failed:', err) }) }

sendCommand('sensor-42', 'reset_calibration')

With HTTP, achieving the same bi-directional communication requires a webhook receiver on the device side — which means the device needs a public IP or a reverse tunnel. That's a significant complexity and security burden.

Battery Implications

Connection establishment is the single largest energy consumer in most radio stacks. WiFi association + DHCP + TLS handshake on an ESP32 typically consumes 300–500 mA for 1–3 seconds. Deep sleep draws 10–150 µA.

For battery-powered devices that wake every 5 minutes, HTTP over TLS is workable — the radio is on for ~2 seconds per cycle. For devices needing sub-minute intervals, a persistent MQTT connection costs a continuous ~20 mA in idle listen mode, which is often cheaper than repeated reconnection overhead.

Rule of thumb:

  • Interval > 5 minutes → wake-sleep with HTTP or MQTT reconnect
  • Interval < 2 minutes → persistent MQTT connection
  • HTTP Where It Belongs: Device Provisioning and Bulk Uploads

    HTTP isn't wrong — it's just wrong for telemetry. We use it extensively for:

  • Device provisioning: REST API call to register a new device, fetch its certificate, write to flash
  • OTA firmware: HTTP chunked download is battle-tested and widely supported
  • Batch uploads: Devices that store data offline and bulk-POST on reconnection
  • Webhook callbacks: Third-party integrations that expect HTTP
  • // Node.js: Express endpoint to accept batch sensor upload
    app.post('/api/v1/readings/batch', express.json({ limit: '1mb' }), async (req, res) => {
      const { deviceId, readings } = req.body
      // readings: [{ ts, temperature, humidity }, ...]

    if (!readings?.length) return res.status(400).json({ error: 'Empty batch' })

    const points = readings.map(r => ({ measurement: 'environment', tags: { deviceId }, fields: { temperature: r.temperature, humidity: r.humidity }, timestamp: new Date(r.ts), }))

    await influxWriteApi.writePoints(points) await influxWriteApi.flush()

    res.json({ accepted: points.length }) })

    The Hybrid Architecture

    Production systems at CodeCaracal almost always use both protocols. The decision tree:

    Is the device battery-powered AND sends data > every 5 min?
      ├─ YES → persistent MQTT with QoS 0 telemetry
      └─ NO  → wake-sleep, evaluate interval:
                < 2 min  → persistent MQTT
                > 5 min  → HTTP POST or MQTT reconnect

    Does the system need cloud-to-device commands? └─ YES → MQTT required (subscribe to command topic)

    Is this firmware OTA or bulk historical upload? └─ YES → HTTP

    Is this device registration or provisioning? └─ YES → HTTP REST

    Real Benchmarks: What We've Seen in Production

    Across systems we've deployed for industrial and agricultural clients:

    | Metric | MQTT (QoS 0) | HTTPS POST | |--------|-------------|------------| | Message overhead | 4–10 bytes | 800–2000 bytes | | Latency (LTE, connected) | 8–20 ms | 300–800 ms | | Battery (ESP32, 30s interval) | ~12 mA avg | ~18 mA avg | | Cloud egress cost | Very low | Moderate | | Broker/infra complexity | Moderate | Low |

    The 8–20 ms MQTT latency assumes a maintained connection. MQTT with a fresh reconnect approaches HTTP latency, so connection pooling matters.

    AWS IoT Core: The Managed Middle Ground

    If you're deploying on AWS, IoT Core speaks MQTT natively and also exposes an HTTPS endpoint for devices that can't maintain persistent connections. This hybrid endpoint is excellent for low-cost cellular modules that only support HTTP. The payload format is identical — you get the same Rules Engine routing regardless of which transport the device uses.

    For a full walkthrough of connecting ESP32 to AWS IoT Core over MQTT, see the [ESP32 MQTT AWS IoT Core Production Guide](/blog/esp32-mqtt-aws-iot-core-production-guide).

    Making the Final Call

    Use MQTT when:

  • Devices publish frequently (< 5-minute intervals)
  • You need cloud-to-device commands
  • Bandwidth or battery is constrained
  • You want QoS guarantees at protocol level
  • Use HTTP when:

  • Devices wake rarely (> 10-minute intervals)
  • You're integrating with third-party systems that expect REST
  • You're doing OTA or provisioning
  • Your team is more REST-native and broker ops overhead isn't justified
  • Use both when (the honest answer for most production systems): your architecture has multiple device classes with different requirements.

    Need help with IoT protocol architecture? [Contact Code Caracal](/contact) — we've shipped these systems for clients across 15+ countries.

    Written by CodeCaracal Engineering

    We write from production experience — every technique in our articles has been deployed to real clients. No academic theory.

    More Articles

    Business · 12 min read

    IoT Device Compliance: FCC, CE, and Product Certification Guide for Hardware Startups

    Business · 11 min read

    What to Look for When Hiring an IoT Development Partner: 8 Critical Criteria

    Business · 11 min read

    IoT MVP to Production: Realistic Timeline and Budget for Hardware Startups

    Business · 11 min read

    IoT Development Agency vs Building In-House: A Decision Framework for Founders

    IoT Dashboard · 13 min read

    Next.js IoT Analytics Dashboard: From Sensor Data to Production App

    Business · 11 min read

    How Much Does It Cost to Build an IoT Product in 2024? A Realistic Breakdown

    IoT Dashboard · 11 min read

    IoT Dashboard UX: Design Principles for Industrial Monitoring Interfaces

    IoT Dashboard · 12 min read

    Node.js WebSocket Server: The Real-Time Backend for IoT Dashboards

    Cloud & DevOps · 12 min read

    Containerizing IoT Backend Services with Docker: From Dev to Production

    IoT Dashboard · 14 min read

    Grafana + InfluxDB IoT Monitoring: Complete Production Setup Guide

    IoT Dashboard · 12 min read

    Building Real-Time IoT Dashboards with React and Recharts

    Cloud & DevOps · 13 min read

    CI/CD for Embedded Firmware: Automated Build, Test, and OTA Release Pipeline

    Mobile Development · 12 min read

    Flutter Offline-First IoT Apps: Hive + Sync Architecture That Works in the Field

    Cloud & DevOps · 14 min read

    Terraform for IoT Infrastructure: Provisioning AWS IoT Core, Lambda, and InfluxDB as Code

    Mobile Development · 10 min read

    Flutter IoT Alerts: Firebase Push Notifications for Device Events

    Cloud & DevOps · 12 min read

    Deploying IoT Backends on AWS: ECS Fargate vs Lambda vs EC2 Decision Guide

    Mobile Development · 11 min read

    Flutter + MQTT: Building Production IoT Mobile Apps That Scale

    Mobile Development · 13 min read

    Flutter BLE: Building a Bluetooth IoT Controller App from Scratch

    Cloud & DevOps · 13 min read

    AWS IoT Core vs Azure IoT Hub vs Google Cloud IoT: 2024 Honest Comparison

    IoT Engineering · 13 min read

    Kafka vs RabbitMQ for IoT: Choosing the Right Message Queue for High-Volume Telemetry

    IoT Engineering · 14 min read

    IoT System Testing: Unit, Integration, Hardware-in-the-Loop, and End-to-End

    IoT Engineering · 14 min read

    Predictive Maintenance with IoT Sensor Data: From Threshold to Machine Learning

    Embedded Systems · 14 min read

    IoT Bootloader Design: Secure Boot, A/B Partitions, and Reliable OTA Recovery

    IoT Engineering · 14 min read

    Multi-Tenant IoT Platform Architecture: Isolation, Scaling, and Data Partitioning

    Embedded Systems · 14 min read

    Memory Management in Embedded Firmware: Avoiding Heap Fragmentation and Stack Overflows

    IoT Engineering · 13 min read

    IoT Cost Optimization: How We Cut AWS IoT Bills by 60% Without Sacrificing Reliability

    IoT Engineering · 12 min read

    Edge Computing in IoT: When to Process On-Device vs In the Cloud

    IoT Engineering · 13 min read

    Digital Twins for IoT: Building a Virtual Mirror of Your Physical Devices

    Embedded Systems · 14 min read

    ESP32 Deep Sleep Mastery: Cutting Power Consumption from 240mA to 10µA

    IoT Engineering · 10 min read

    MQTT QoS 0, 1, and 2 Explained: Choosing the Right Level for IoT

    IoT Engineering · 14 min read

    IoT Monitoring and Observability: Metrics, Logs, and Distributed Tracing

    Embedded Systems · 14 min read

    Debugging Embedded Firmware: JTAG, GDB, Logic Analyzers, and Serial Tracing

    IoT Engineering · 12 min read

    WebSocket vs MQTT vs Server-Sent Events: Real-Time IoT Protocol Deep Dive

    Embedded Systems · 13 min read

    STM32 HAL vs Low-Level Drivers: When the Abstraction Costs You Too Much

    IoT Engineering · 13 min read

    IoT Data Pipeline: From Raw Sensor Reading to Live Dashboard in Under 100ms

    IoT Engineering · 13 min read

    Zero-Touch IoT Device Provisioning: Scaling from 10 to 100,000 Devices

    Embedded Systems · 13 min read

    UART vs SPI vs I2C: Choosing the Right Protocol for Sensor Integration

    IoT Engineering · 12 min read

    Real-Time IoT Alerting: From Simple Thresholds to ML Anomaly Detection

    Embedded Systems · 12 min read

    ESP32 Partition Table: Designing Flash Layout for Production Firmware

    IoT Engineering · 12 min read

    IoT Architecture Patterns: Hub-and-Spoke, Mesh, and Edge-Cloud Hybrid

    Embedded Systems · 13 min read

    IoT Battery Life Optimization: Engineering Devices That Last Years on a Single Charge

    IoT Engineering · 13 min read

    Time-Series Databases for IoT: InfluxDB vs TimescaleDB vs AWS Timestream

    Security · 14 min read

    Zero-Trust Security for Embedded IoT: Why Your Devices Are Probably Vulnerable

    Embedded Systems · 14 min read

    FreeRTOS on ESP32: Task Scheduling, Queues, and Resource Management for IoT

    IoT Engineering · 12 min read

    Building a Production IoT Gateway with Raspberry Pi and Node.js

    Embedded Systems · 13 min read

    ESP32 vs STM32: Choosing the Right Microcontroller for Your IoT Project

    Mobile Development · 10 min read

    Flutter + WebSocket: Building Real-Time IoT Dashboards That Don't Stutter

    IoT Engineering · 13 min read

    IoT Fleet Management at Scale: AWS IoT Core Device Registry and Provisioning

    IoT Engineering · 12 min read

    ESP32 → MQTT → AWS IoT Core: The Production-Grade Architecture Guide

    Let's Build Together

    Got an IoT challenge?
    We've shipped it.

    Whether you need a fleet to track, a factory to monitor, or a farm to automate — our team has done it before and we'd love to build it with you. Typical response time: under 24 hours.

    No upfront commitment99.9% uptime SLANDA on requestFixed-price options