If you have used MQTT 3.1.1 for a few years and are wondering what MQTT 5 actually changes, or if you are starting a new deployment and need to decide between the two, this is the article that gets you there directly. The two versions share the same core protocol — publish/subscribe over TCP, three QoS levels, retained messages, last will and testament, persistent sessions — but MQTT 5 adds a set of features and behavioral refinements that change how you design, debug, and operate a real system.
This article compares MQTT 5 and MQTT 3.1.1 side by side, feature by feature and behavior by behavior, with side trips into the practical implications of each difference. It is structured for scanning, with comparison tables in every section and decision-oriented summaries at the end. For deep dives on any individual MQTT 5 feature, the dedicated articles in this category go further.
At a glance
| Aspect | MQTT 3.1.1 | MQTT 5 |
|---|---|---|
| Standardized | OASIS, October 2014 | OASIS, March 2019 |
| Wire-level protocol version byte | 4 | 5 |
| Wire compatibility | — | Not wire-compatible with 3.1.1; brokers can support both side by side |
| Control packet types | 14 | 15 (adds AUTH) |
| Packet properties | None — flags only | Yes — typed key-value properties on most packets |
| User properties (application metadata) | No | Yes |
| Reason codes | 6 (only on CONNACK) | 20+ across most acknowledgement packets |
| Reason strings (human-readable) | No | Yes (optional) |
| Server-initiated DISCONNECT | No | Yes, with reason code |
| Session model | Clean Session flag | Clean Start + Session Expiry Interval |
| Session lifetime | Indefinite once persistent | Bounded by Session Expiry Interval |
| Message expiry | No | Yes, per-PUBLISH (Message Expiry Interval) |
| Capability discovery | No | Yes (CONNACK properties) |
| Topic aliases | No | Yes (per-connection alias for long topics) |
| Shared subscriptions | Vendor-specific extensions only | Standardized ($share/<group>/<topic>) |
| Request/response | Ad-hoc patterns in application | Standardized (Response Topic, Correlation Data) |
| Flow control | No protocol-level limit | Receive Maximum bounds in-flight QoS 1/2 publishes |
| Enhanced authentication | No (username/password only at protocol level) | AUTH packet supports challenge/response (SASL, OAuth tokens) |
| Will Delay | No | Yes (delay before publishing the will) |
| Retransmission on healthy connection | Allowed | Removed (retransmit only after reconnect) |
| Username without password | Required if password set | Password may be sent without username |
The rest of this article walks through these differences in detail, grouped by what they affect.
Table of Contents
Core protocol elements that did not change
Before listing the differences, it is worth stating what stayed the same. MQTT 5 is an evolution, not a rewrite, and any developer fluent in MQTT 3.1.1 already understands most of MQTT 5.
| Element | Status across versions |
|---|---|
| Publish/subscribe model with broker in the middle | Unchanged |
Topic structure (UTF-8 hierarchical strings with / separator) | Unchanged |
Single-level + and multi-level # wildcards | Unchanged |
| Three QoS levels (0, 1, 2) with the same handshakes | Unchanged |
| Per-hop QoS negotiation and downgrade | Unchanged |
| Retained messages, last known value per topic | Unchanged |
| Last Will and Testament mechanism | Unchanged (refined; see below) |
| Keep alive with PINGREQ and PINGRESP | Unchanged |
| TCP/IP transport, ports 1883/8883, WebSockets | Unchanged |
| Binary packet format with fixed header | Unchanged |
If you know how the pub/sub model, QoS handshakes, retained messages, and keep alive work in MQTT 3.1.1, you know how they work in MQTT 5 too. The new material in MQTT 5 sits on top of this foundation rather than replacing it.
Why the version number jumped from 3.1.1 to 5
A short note worth getting out of the way: there was never an “MQTT 4.” The protocol version is encoded as a single byte in the CONNECT packet. MQTT 3.1 sends value 3 on the wire; MQTT 3.1.1 sends value 4. To make the version name match the wire-level value, the next version uses 5 in both places. The naming is mechanical, not semantic.
Differences in error reporting and diagnostics
This is the area where MQTT 5 most directly improves the day-to-day experience of running MQTT.
| Capability | MQTT 3.1.1 | MQTT 5 |
|---|---|---|
| Number of CONNACK return/reason codes | 6 | 20+ |
| Reason codes on PUBACK | No | Yes |
| Reason codes on PUBREC, PUBREL, PUBCOMP | No | Yes |
| Reason codes on SUBACK | One per topic (success/failure) | One per topic with specific reason |
| Reason codes on UNSUBACK | No | Yes, one per topic |
| Reason codes on DISCONNECT | No (no broker-side DISCONNECT exists) | Yes, in either direction |
| Reason string (human diagnostics) | No | Yes (optional) |
| Server-initiated DISCONNECT with explanation | No (broker just closes the socket) | Yes |
The practical effect: in MQTT 3.1.1, failure modes often look identical to the client. A publish that the broker refused, a publish that nobody was subscribed to, and a publish that succeeded all look the same. The broker closing the connection because of a protocol violation looks the same as the network failing. In MQTT 5, the broker can tell the client what happened, with a specific reason code and (optionally) a human-readable string. This is covered in detail in the Improved Client Feedback and Negative Acknowledgements article.
Differences in session management
MQTT 5 replaces MQTT 3.1.1’s single cleanSession flag with a two-control model and adds a session lifetime that the protocol bounds.
| Capability | MQTT 3.1.1 | MQTT 5 |
|---|---|---|
| Session persistence flag | cleanSession (boolean) | Clean Start (boolean) |
| Session lifetime | Indefinite once cleanSession = false | Set by Session Expiry Interval (seconds) |
| Session lifetime change at disconnect | Not possible | Can update Session Expiry Interval in DISCONNECT |
| Maximum session lifetime | Implementation-defined (no protocol bound) | Up to 4,294,967,295 seconds (about 136 years) |
| Per-message TTL (Message Expiry) | No | Yes, set on each PUBLISH |
Mapping the old behavior to the new: cleanSession = true in MQTT 3.1.1 maps to Clean Start = true with Session Expiry Interval = 0 in MQTT 5 (start fresh, end with the connection). cleanSession = false in MQTT 3.1.1 maps to Clean Start = false with a large Session Expiry Interval, though in practice the better default in MQTT 5 is to pick a finite lifetime that matches the device’s expected offline pattern rather than the effectively-unbounded maximum. Both mechanisms are covered in the Persistent Sessions and Session and Message Expiry Intervals articles.
Differences in metadata and payload description
This is where MQTT 5’s properties machinery is most visible. MQTT 3.1.1 had no place for application metadata at the protocol level; MQTT 5 adds it.
| Capability | MQTT 3.1.1 | MQTT 5 |
|---|---|---|
| User properties (arbitrary key-value) | No | Yes, on most packet types |
| Payload format indicator (binary vs. UTF-8) | No | Yes |
| Content type (MIME-style) | No | Yes |
| Response topic and correlation data (for request/response) | No (must be encoded by application) | Yes (protocol-defined) |
| Subscription identifiers | No | Yes (let subscriber tag its subscriptions) |
In MQTT 3.1.1, application metadata had to be encoded into the topic name (turning routing into a description) or into the payload (forcing every consumer to parse an envelope). MQTT 5 gives metadata a designated place where it does not compromise either. The User Properties and Payload Format Description articles cover this in detail.
Differences in scalability features
| Capability | MQTT 3.1.1 | MQTT 5 |
|---|---|---|
| Shared subscriptions | Vendor-specific extension on some brokers | Standardized ($share/<group>/<topic>) |
| Topic aliases (short integer for long topic) | No | Yes (per-connection alias) |
| Capability discovery (what the broker supports) | No | Yes (CONNACK properties) |
| Maximum packet size negotiation | No | Yes (CONNACK Maximum Packet Size) |
| Flow control (bound in-flight QoS 1/2) | No (sender can flood receiver) | Yes (Receive Maximum) |
Shared subscriptions deserve a specific note: load-balancing message delivery across consumer instances was something users had been doing in MQTT 3.1.1 with vendor-specific broker features (the syntax and behavior varied). MQTT 5 standardized the mechanism, which means a multi-consumer load-balancing setup that works on one MQTT 5 broker will work on another.
Flow control is also a new safety mechanism. In MQTT 3.1.1, there was nothing stopping a publisher from sending thousands of QoS 1 or 2 publishes faster than the broker (or the broker faster than a subscriber) could process them, leading to memory pressure or dropped connections. MQTT 5’s Receive Maximum lets each side bound how many unacknowledged publishes the other may have in flight; if exceeded, the sender gets a clear protocol error rather than mysterious failures. The Flow Control article covers this.
Differences in authentication and security
| Capability | MQTT 3.1.1 | MQTT 5 |
|---|---|---|
| Username and password in CONNECT | Yes (username required if password set) | Yes (password may be sent without username) |
| TLS for transport security | Yes (operational, not protocol-level) | Yes (operational, not protocol-level) |
| Multi-step challenge/response authentication | No | Yes (new AUTH packet) |
| Re-authentication during a session | No | Yes (via AUTH packet) |
| Standard support for SCRAM, Kerberos, OAuth tokens | No | Yes (via AUTH and SASL mechanisms) |
MQTT 3.1.1’s authentication was minimal: send a username and password in CONNECT, succeed or fail. Real-world deployments routinely wanted more (challenge-response with mutual proof, token-based authentication, the ability to re-authenticate without dropping the connection), and built it outside the protocol. MQTT 5’s new AUTH packet supports a full SASL-style multi-step exchange and re-authentication mid-session, covered in the Enhanced Authentication article.
Differences in Last Will and Testament
The LWT mechanism is the same in both versions at its core: a will registered in CONNECT, published by the broker on ungraceful disconnect. MQTT 5 refines it in a few ways.
| Capability | MQTT 3.1.1 | MQTT 5 |
|---|---|---|
| Will fields (topic, payload, QoS, retain) | Yes | Yes |
| Will Delay Interval (delay before publishing) | No | Yes |
| Will properties (user properties, content type, etc.) | No | Yes |
| Will on graceful DISCONNECT | Always suppressed | Optionally triggered via reason code 0x04 |
| Will Message Expiry Interval | No | Yes |
The Will Delay Interval is particularly useful. In MQTT 3.1.1, every brief network glitch that reaches the keep alive timeout causes a will to fire, which is often not what was wanted. In MQTT 5, the broker waits the configured delay before publishing; if the client reconnects within that time, the will is canceled. Covered in the Last Will and Testament article.
Behavioral changes you should know about
Some MQTT 5 changes are not about new features but about how the protocol behaves. These can catch you off guard during migration.
No retransmission on a healthy connection. In MQTT 3.1.1, an unacknowledged QoS 1 or 2 PUBLISH could be retransmitted while the connection was still open, on an implementation-defined timer. In practice this often made things worse: a slow client got hit with duplicate publishes before it had finished processing the original. MQTT 5 removes the requirement for timer-based retransmission during an active TCP connection; retransmission happens only after a reconnect, when unacknowledged packets are sent again as part of resuming the session. Systems that relied on in-connection retransmission need to be reviewed.
Sessions can now expire on their own. A client with cleanSession = false in MQTT 3.1.1 could be confident that its session would persist as long as the broker was running. In MQTT 5, the broker honors the Session Expiry Interval and can discard a session after disconnect even if no one wipes it explicitly. A client that does not return within its session expiry comes back to a fresh session, with session present = false in the CONNACK.
Retransmission is bounded by Receive Maximum. With MQTT 5’s flow control, a sender that tries to publish more unacknowledged QoS 1/2 messages than the other side’s Receive Maximum allows will be disconnected with reason code 0x93 (Receive Maximum Exceeded). MQTT 3.1.1 had no equivalent limit; sending too aggressively in 3.1.1 was often a quality-of-service problem, but did not produce a protocol error.
ClientId conflicts trigger take-over either way. This behavior did not change, but it is worth knowing in both directions: when a new connection arrives with a ClientId already in use, the broker closes the existing connection. This was true in MQTT 3.1.1 and remains true in MQTT 5. The Keep Alive and Client Take-Over article covers this.
Capability advertisements are best read on every connect. A client connecting to a broker for the first time, or after the broker has been reconfigured, may find different capabilities or limits than it expected. MQTT 5 clients should treat the CONNACK as authoritative for capability and limit information on every connect, not just the first.
What the upgrade actually requires
For a deployment to use MQTT 5, three things must change:
- The broker must be MQTT 5 capable. Most modern MQTT brokers support MQTT 3.1.1 and MQTT 5 side by side, accepting either on the same port. Verify that the broker version you are running speaks MQTT 5.
- Each client must be on a library that supports MQTT 5. The client library is what builds the packets and parses the responses. A client built on an MQTT 3.1.1-only library cannot speak MQTT 5 even if the broker supports it; the library has to know how.
- Application code may need to change. Most basic MQTT operations (connect, publish, subscribe, callbacks) work the same way at the API level. Behavioral changes (no in-connection retransmission, session expiry, capability advertisements) and new features (user properties, reason codes, request/response) need application-level handling to be useful.
The good news is that mixed deployments are common and supported. You can run MQTT 5-capable brokers, migrate clients to MQTT 5 in waves as their firmware allows, and have both versions coexisting indefinitely. There is no flag day required.
When to choose each version
For most new deployments today, MQTT 5 is the right default. The improved diagnostics alone justify it for any system that has to be operated by humans rather than autonomously. The new features that matter most depend on the use case.
Choose MQTT 5 if you need any of:
- Better diagnostics (reason codes, reason strings, server-side DISCONNECT with explanation).
- Bounded session and message lifetimes (Session Expiry and Message Expiry Intervals).
- Application-level metadata on messages (user properties, content type, payload format).
- Load balancing across consumer groups (shared subscriptions).
- Reduced bandwidth for long topic names (topic aliases).
- Standardized request/response semantics on top of pub/sub.
- Modern authentication (SCRAM, Kerberos, OAuth tokens) via the AUTH packet.
- Protocol-level flow control to bound in-flight publishes.
Choose MQTT 3.1.1 only if any of:
- You have a large existing population of devices that cannot easily be updated to MQTT 5 clients.
- A specific component in your toolchain does not yet support MQTT 5 well (some older brokers, libraries, or hosted services lag behind).
- The MQTT 5 features above genuinely solve no problem in your deployment, and the migration effort is not justified.
The migration cost is real but bounded. Brokers commonly support both versions; library updates are usually straightforward; the behavioral changes can be reviewed against your specific design. For deployments large enough to have problems that MQTT 3.1.1 cannot solve, MQTT 5 is worth the work.
MQTT 3.1.1 patterns to retire on MQTT 5
If you are coming from MQTT 3.1.1, you have probably built application-level workarounds for things MQTT 3.1.1 could not do. Several of those workarounds become obsolete under MQTT 5 and should be retired during migration, because keeping them adds complexity without benefit.
| Pattern from MQTT 3.1.1 | What to use in MQTT 5 instead |
|---|---|
| Encoding metadata into topic names (version, source, units) | User properties on the PUBLISH |
| Wrapping payloads in a JSON envelope for metadata | User properties plus content type / payload format indicator |
| Application-level acknowledgement protocols built on extra topics | Standardized request/response with Response Topic and Correlation Data |
| Heartbeat publishes to detect device liveness | Last Will and Testament with the retained-status pattern (the same pattern works in 3.1.1, but in 5 the Will Delay Interval reduces false alarms) |
| Cron jobs cleaning up stale persistent sessions on the broker | Session Expiry Interval, set per client |
| Manually purging old retained messages from defunct devices | Message Expiry Interval on retained publishes |
| Vendor-specific shared subscription extensions | Standard $share/<group>/<topic> subscriptions |
| Polling for broker capabilities by trial and error | CONNACK capability advertisements, read on every connect |
This is not an exhaustive list, but each of these is a workaround so common in MQTT 3.1.1 deployments that you likely have at least one of them in production code. Identifying which ones MQTT 5 supersedes is one of the highest-value parts of a migration: you do not just gain features, you also get to delete workarounds.
A migration checklist
If you decide to migrate an MQTT 3.1.1 deployment to MQTT 5, the work breaks down roughly as follows.
- Upgrade the broker first, with both MQTT 3.1.1 and MQTT 5 enabled. Existing clients continue to connect on 3.1.1 unchanged.
- Verify broker behavior under load with realistic client traffic, including the capability advertisements the broker chooses to expose. Configure server-side maximums (Session Expiry, Maximum Packet Size, Receive Maximum) deliberately rather than accepting defaults.
- Pick one class of client to migrate first, ideally one whose firmware is easy to update and whose behavior is well understood. Update its library to MQTT 5 and verify it connects, publishes, and subscribes correctly.
- Review behavioral changes for that client class. The most important: it will no longer experience in-connection retransmission, and its session lifetime is now governed by the Session Expiry Interval you set rather than being unbounded.
- Start using MQTT 5 features incrementally. Read the CONNACK’s capability advertisements. Add reason code handling to acknowledgement processing. Use Session Expiry deliberately. Add user properties only where they earn their place (do not over-tag every message).
- Migrate other client classes in waves. There is no need to migrate everything at once; each class can move on its own timeline.
- Eventually decommission MQTT 3.1.1 support on the broker, once no clients remain on the old version. This step is optional; many deployments leave both versions enabled permanently.
A note on testing: MQTT 5’s behavioral changes (no in-connection retransmission, session expiry, flow control limits) can surface in ways that MQTT 3.1.1 testing would not have caught. Stress-test new MQTT 5 deployments with realistic disconnect-and-reconnect patterns to make sure the session model matches what the application actually needs.
Frequently asked questions
Is MQTT 5 backward compatible with MQTT 3.1.1?
Not at the wire level. An MQTT 3.1.1 client and an MQTT 5 broker can talk only if the broker explicitly supports both versions, which most modern brokers do. Coexistence is supported indefinitely.
Do I have to migrate from MQTT 3.1.1 to MQTT 5?
No. MQTT 3.1.1 is still an OASIS standard and remains widely deployed. Migration is worthwhile when the new MQTT 5 features solve real problems in your deployment.
What is the single biggest difference?
Structurally, the introduction of packet properties as a generic metadata mechanism, since almost every new feature is implemented as a property. Operationally, the expanded reason codes and reason strings, which make MQTT systems much easier to debug.
Why is there no MQTT 4?
Because the protocol version is encoded as a single byte in the CONNECT packet, and MQTT 3.1.1 already used the value 4 on the wire. MQTT 5 was named to match its wire-level byte value of 5.
Will my MQTT 3.1.1 client work against an MQTT 5 broker?
Almost certainly yes, if the broker supports both versions (most do). The client connects as 3.1.1 and behaves as 3.1.1; it does not magically gain MQTT 5 features.
Should I use MQTT 5 for new IoT projects?
Yes, in most cases. The improved diagnostics, session and message expiry, and the protocol’s capability advertisements are valuable from day one. Use MQTT 3.1.1 only if a specific constraint forces it.
Are shared subscriptions in MQTT 5 the same as the broker-specific extensions I had in MQTT 3.1.1?
The mechanism is similar but now standardized. MQTT 5’s $share/<group>/<topic> is a protocol-defined syntax that works across compliant brokers, where MQTT 3.1.1’s equivalent features were vendor-specific extensions with varying syntax.
Can MQTT 5 messages be smaller than MQTT 3.1.1 messages?
Yes, in some cases. MQTT 5’s topic alias feature lets a long topic name be replaced by a short integer for the duration of a connection, which can reduce bandwidth significantly on systems with long topic names and frequent publishes.
Does MQTT 5 change how QoS works?
The three levels and their semantics are unchanged. What changes is that retransmission during a healthy connection is no longer required (it happens only after reconnect), the acknowledgement packets now carry reason codes, and Receive Maximum bounds the number of unacknowledged QoS 1/2 publishes in flight.
