Every MQTT packet has a size — from a tiny 2-byte PINGREQ to a large PUBLISH carrying a firmware update. In MQTT 3.1.1, receivers had no way to tell senders what maximum packet size they could handle. A memory-constrained embedded gateway would just crash when a 10 MB PUBLISH arrived. A publisher had to guess: is this Server OK with big messages? A wrong guess meant unpredictable failures.
MQTT 5 fixed this with the Maximum Packet Size property. Both sides declare during connection setup exactly how large a packet they will accept. Violation triggers a clear DISCONNECT with Reason Code 0x95 (Packet too large) — no more mystery failures, no more guessing. Combined with Receive Maximum (which limits in-flight message count), Maximum Packet Size gives receivers full control over their memory commitment.
This article explains how Maximum Packet Size works, what actually counts toward the limit (spoiler: the whole packet, not just the payload), how to tune it for embedded through high-throughput deployments, and how it interacts with other MQTT 5 features.
Table of Contents
What Maximum Packet Size is in one paragraph
Maximum Packet Size is a UINT32 property (0x27) that a party declares during MQTT 5 connection setup to specify the largest packet in bytes it will accept from the other party. The client sends it in CONNECT — telling the Server “don’t send me packets larger than N bytes.” The Server sends it in CONNACK — telling the client “don’t send me packets larger than M bytes.” Values are independent per direction. If a sender exceeds the receiver’s declared limit, the receiver responds with DISCONNECT carrying Reason Code 0x95 (Packet too large) and closes the connection. The limit applies to the entire MQTT packet — Fixed Header + Variable Header + Payload — not just the payload data. Absence of this property means “no explicit limit,” subject to the MQTT protocol’s absolute maximum of 268,435,455 bytes (~256 MB) derived from the Variable Byte Integer encoding of the Remaining Length field.
Why MQTT 5 added explicit packet size limits
MQTT 3.1.1 had no packet size property. This caused several categories of pain in production:
Pain 1: Unpredictable failures
An embedded gateway with 128 KB of RAM connects to a broker. Everything works fine — until one day someone publishes a 500 KB message. The gateway allocates memory to receive the packet, runs out of RAM, and either crashes or drops the connection silently. Nothing in the protocol warned the publisher that this was a problem.
Pain 2: Implementation-dependent behavior
Different MQTT 3.1.1 brokers had different implicit limits. Mosquitto might accept 256 MB packets by default; some embedded brokers might refuse packets over 8 KB. A client that worked fine against one broker might mysteriously fail against another with no protocol-level explanation.
Pain 3: No way to enforce policy
Cloud MQTT services wanting to prevent abuse (huge payloads consuming bandwidth) had no MQTT-native way to tell clients “you can only send messages up to X KB.” They had to enforce limits at higher layers (TLS termination, application layer) with confusing failure modes.
Pain 4: Memory planning was blind
Sizing a broker for expected traffic required knowing the largest possible message. Without a way to enforce that, brokers had to provision for worst-case scenarios — wasting memory in typical deployments and still risking failures in extreme cases.
MQTT 5’s Maximum Packet Size fixed all four: explicit per-connection declaration, clear failure mode when exceeded, portable across implementations, enforced at the protocol layer.
The Property (0x27) — the mechanism
Maximum Packet Size is one of the connection-negotiated properties exchanged during CONNECT/CONNACK.
| Field | Value |
|---|---|
| Property Identifier | 0x27 (39 decimal) |
| Type | UINT32 (4 bytes, big-endian) |
| Valid range | 1 to 268,435,455 (~256 MB) |
| Invalid value | 0 (triggers Protocol Error 0x82) |
| Absence | No explicit limit (subject to protocol absolute max) |
| Appears in | CONNECT (client→Server), CONNACK (Server→client) |
Direction semantics
This is where engineers often confuse themselves:
- Client’s Maximum Packet Size in CONNECT = the largest packet the Server may send to this client
- Server’s Maximum Packet Size in CONNACK = the largest packet the client may send to the Server
The two values are independent. A memory-constrained embedded device might declare Maximum Packet Size = 4096 (refusing large inbound packets) while the Server declares Maximum Packet Size = 1048576 (accepting large outbound packets from clients). Each direction is governed separately.
How the sender uses it
The sender is responsible for tracking the declared limit and ensuring every packet it sends fits within it. Before serializing a PUBLISH, the sender computes the total encoded size:
Total packet size = Fixed Header + Variable Header + Payload
If this exceeds the receiver’s declared Maximum Packet Size, the sender must either:
- Refuse to send the packet (fail at the application layer)
- Split the payload into multiple smaller messages
- Compress the payload if possible
- Use Topic Aliases to reduce Variable Header size (for repeated topics)
The sender must not send an oversized packet — the spec doesn’t allow “try it and see.”
Default when absent
If Maximum Packet Size is absent from CONNECT or CONNACK, no explicit limit applies. The sender is still bounded by the absolute MQTT protocol maximum (~256 MB), but there’s no smaller per-connection limit.
Most well-designed MQTT 5 servers declare a Maximum Packet Size in CONNACK — even a generous one — to protect against memory-exhaustion attacks by malformed clients.
What actually counts toward packet size
This trips up many engineers. Maximum Packet Size limits the entire MQTT packet, not just the payload. Here’s what’s included:
For a PUBLISH packet
FIXED HEADER
Byte 1: Packet type + flags 1 byte
Bytes 2-5: Remaining Length (VBI) 1-4 bytes
VARIABLE HEADER
Topic Name: UTF-8 String 2 + N bytes
Packet Identifier: (QoS > 0 only) 2 bytes
Properties Length: Variable Byte Integer 1-4 bytes
Properties: Multiple properties varies
PAYLOAD
Application data varies
For a CONNECT packet
FIXED HEADER
Byte 1: Packet type 1 byte
Bytes 2-5: Remaining Length 1-4 bytes
VARIABLE HEADER
Protocol Name: "MQTT" as UTF-8 6 bytes
Protocol Version: 5 1 byte
Connect Flags: 1 byte
Keep Alive: 2 bytes
Properties Length: 1-4 bytes
Properties: varies
PAYLOAD
Client Identifier (UTF-8 String) varies
Will Properties (if Will flag set) varies
Will Topic (if Will flag set) varies
Will Payload (if Will flag set) varies
User Name (if User Name flag) varies
Password (if Password flag) varies
The “hidden” overhead
Engineers often calculate packet size by looking at payload size and forget these contributors:
- Long topic names —
factory/site-A/line-3/station-5/sensor-42/temperatureis 51 bytes alone - User Properties — each name+value pair adds ~50-200 bytes typically
- Response Topic — for request/response, adds the response topic string
- Correlation Data — up to 65,535 bytes
- Authentication Data — for Enhanced Authentication, can be large
- Reason String — for negative acknowledgements, adds human-readable text
- Subscription Identifiers — Property 0x0B for advanced routing
For a 100-byte payload PUBLISH with:
- Topic name (30 bytes)
- 3 User Properties (~150 bytes)
- Response Topic (25 bytes)
- Correlation Data (16 bytes)
- Standard properties overhead (~10 bytes)
The total encoded packet is roughly 331 bytes — over 3× the payload size.
Wildcards NOT applicable
Maximum Packet Size doesn’t check compressed size, TLS/TCP overhead, or anything below the MQTT layer. It’s strictly the encoded MQTT packet size before TLS encryption.
Failure mode — Reason Code 0x95 (Packet too large)
If a sender exceeds the receiver’s Maximum Packet Size, the receiver responds with DISCONNECT:
Receiver → Sender: DISCONNECT
Reason Code: 0x95 (Packet too large)
Properties (optional):
Reason String: "Packet size 8500 bytes exceeds Maximum Packet Size of 4096"
The TCP connection is closed. The sender must reconnect to resume operation.
Sender responsibility vs receiver enforcement
Like Flow Control’s Receive Maximum, the primary responsibility is the sender’s: track the receiver’s declared limit and never exceed it. The receiver’s enforcement (DISCONNECT 0x95) is a safety net — a well-behaved sender never triggers this.
If your client regularly hits 0x95, either:
- Your client isn’t tracking Maximum Packet Size at all
- Your client miscalculates packet size (forgetting properties overhead)
- Payloads are legitimately too large for the Server’s declared limit
Fixes depend on which case:
- Not tracking: implement Maximum Packet Size awareness in the client
- Miscalculating: correctly account for all packet components including properties
- Legitimate need: request higher Maximum Packet Size from the Server, or split messages
What if the receiver won’t tell you the limit?
If Maximum Packet Size is absent from CONNACK, the Server hasn’t declared an explicit limit. But this doesn’t mean unlimited — implementation-specific defaults still apply. Sending very large packets is still risky if you don’t know the actual limit.
Best practice: assume conservative defaults (e.g., 128 KB) unless you know your Server can handle larger.
The 256 MB absolute maximum
MQTT’s Remaining Length field uses Variable Byte Integer (VBI) encoding. Each byte contributes 7 bits of value plus a 1-bit continuation flag. VBI supports up to 4 bytes, giving:
Maximum Remaining Length = 128^4 - 1 = 268,435,455 bytes ≈ 256 MB
Adding the 1-byte Fixed Header (packet type + flags):
Absolute maximum MQTT packet size = 268,435,455 + 5 = 268,435,460 bytes ≈ 256 MB
This is the hard ceiling — no MQTT 5 implementation can accept or send packets larger than this. Any Maximum Packet Size property declaring a value larger than 268,435,455 is invalid.
Practical implications
For typical MQTT deployments, the ~256 MB limit is theoretical — no real-world use case sends packets that large. Practical limits are much lower:
- Embedded devices: 4 KB – 64 KB typical
- Cloud IoT platforms: 128 KB – 1 MB typical
- On-premises brokers: 1 MB – 10 MB typical
- Bulk data transfer scenarios: 10 MB – 100 MB
For anything approaching 256 MB, MQTT is arguably the wrong protocol — HTTP with chunked encoding or dedicated file transfer protocols are more appropriate.
Tuning guidance by deployment
The right Maximum Packet Size depends heavily on your use case:
Embedded/constrained IoT devices
| Constraint | Typical Maximum Packet Size |
|---|---|
| Small MCU (< 64 KB RAM) | 1 KB – 4 KB |
| Modest device (128 KB – 512 KB RAM) | 4 KB – 16 KB |
| Higher capacity IoT device (1 MB+ RAM) | 16 KB – 128 KB |
Rule of thumb: allocate ~25% of available RAM for the largest expected message. Leave headroom for MQTT stack state, other buffers, and application code.
Cloud IoT / SaaS brokers
| Service | Typical Maximum Packet Size |
|---|---|
| AWS IoT Core | 128 KB (hard limit, not configurable) |
| Azure IoT Hub | 256 KB (hard limit) |
| Google Cloud IoT Core | (Service deprecated 2023) |
| HiveMQ Cloud | 268 MB (spec absolute max, configurable) |
Cloud services often enforce packet size limits at the protocol layer and reject over-limit connections upfront. Check your provider’s documentation.
Self-hosted brokers
Common defaults:
| Broker | Default Maximum Packet Size |
|---|---|
| HiveMQ Enterprise | 268,435,460 (unlimited, configurable down) |
| EMQX | 1 MB (configurable) |
| Mosquitto | (implementation-specific, historically ~256 MB) |
| VerneMQ | Configurable, no default limit |
Self-hosted brokers give you full control. Common configurations:
- Standard telemetry: 64 KB (fits almost any practical message)
- Rich sensor data: 512 KB (fits images, structured payloads)
- File transfer over MQTT: 10 MB – 100 MB (rare use case)
- Firmware updates: 1 MB – 10 MB per chunk
High-throughput publishers
If your client will publish large payloads (bulk data, images, firmware updates), calculate:
Required Maximum Packet Size =
max_payload_size
+ topic_name_length
+ expected_properties_overhead
+ protocol_overhead (typically ~20 bytes)
For safety, add 20% headroom to account for edge cases.
Memory budget calculation
Combined with Receive Maximum (in-flight limit), the worst-case memory commitment is:
Max receiver memory = Receive Maximum × Maximum Packet Size
For Receive Maximum = 100 and Maximum Packet Size = 1 MB, expect up to ~100 MB of in-flight message data at peak. This is why the two properties work together.
For deeper Flow Control coverage, see our MQTT 5 Flow Control article.
Interaction with Flow Control and other features
Maximum Packet Size interacts with several other MQTT 5 mechanisms:
Flow Control (Receive Maximum)
The two properties together bound receiver memory. Receive Maximum controls how many in-flight messages; Maximum Packet Size controls how big each one can be. Together they enforce a total memory budget.
Topic Aliases
Topic Aliases (Property 0x23) reduce packet size by replacing repeated topic name strings with 2-byte aliases. Especially valuable when your topic names are long and Maximum Packet Size is tight. See our MQTT 5 Topic Aliases article for the mechanics.
Example: a device with Maximum Packet Size = 4096 publishing to a 60-byte topic name saves 58 bytes per message after the first (using aliases). This effectively raises payload capacity from 4020 bytes to 4078 bytes per message.
User Properties
User Properties (Property 0x26) add name-value pair metadata. Each User Property typically adds 5-100+ bytes depending on name and value length. Heavy use of User Properties eats into your Maximum Packet Size budget significantly.
Enhanced Authentication
Authentication Data (Property 0x16) can be substantial for methods like SCRAM-SHA-512 or complex OAuth tokens. Client connections carrying JWT tokens can push CONNECT packets to several KB just for authentication content. Ensure Maximum Packet Size accommodates this.
For details, see our Enhanced Authentication in MQTT 5 article.
Session state
Session state (Session Expiry Interval, in-flight message tracking) is separate from Maximum Packet Size. But large numbers of retained in-flight messages during a resumed session can create bursts of large packets after reconnection — Maximum Packet Size still enforces per-packet limits at that time.
Common calculation examples
Example 1: Simple sensor telemetry
Topic: "sensors/temp/room-42" (2 + 21 = 23 bytes)
Packet Identifier (QoS 1): 2 bytes
Properties Length: 1 byte
Payload: {"value": 23.5, "ts": 12345678} (~30 bytes)
Fixed Header + Remaining Length: 3 bytes
Total encoded packet: ~59 bytes
A Maximum Packet Size of 512 bytes is generous for this pattern.
Example 2: PUBLISH with User Properties
Topic: "orders/created" (2 + 15 = 17 bytes)
Packet Identifier (QoS 1): 2 bytes
Properties Length: 2 bytes
Property: User Property "trace-id"="a1b2c3d4-e5f6-..." (5+42 = 47 bytes)
Property: User Property "tenant-id"="acme-corp" (5+18 = 23 bytes)
Property: User Property "region"="us-west-2" (5+15 = 20 bytes)
Property: Payload Format Indicator = 1 (2 bytes)
Property: Content Type = "application/json" (5+18 = 23 bytes)
Payload: {"order_id": ..., "items": [...], ...} (500 bytes)
Fixed Header + Remaining Length: 3 bytes
Total encoded packet: ~637 bytes
Properties overhead alone is 115 bytes — over 20% of the payload size.
Example 3: Large file transfer
Topic: "device/firmware/chunk" (2 + 22 = 24 bytes)
Packet Identifier (QoS 2): 2 bytes
Properties Length: 3 bytes
Property: Correlation Data (16 bytes UUID) (19 bytes)
Property: User Property "chunk-num"="42" (16 bytes)
Property: User Property "total-chunks"="100" (18 bytes)
Payload: <firmware chunk data> (16,384 bytes)
Fixed Header + Remaining Length: 4 bytes
Total encoded packet: ~16,470 bytes
Need Maximum Packet Size of at least 20 KB with headroom for future property additions.
Example 4: CONNECT with credentials
Fixed Header + Remaining Length: 3 bytes
Protocol Name "MQTT": 6 bytes
Protocol Version 5: 1 byte
Connect Flags: 1 byte
Keep Alive: 2 bytes
Properties Length: 2 bytes
Property: Session Expiry Interval 5 bytes
Property: Receive Maximum 3 bytes
Property: Maximum Packet Size 5 bytes
Property: Topic Alias Maximum 3 bytes
Property: Authentication Method "SCRAM-SHA-256" (18 bytes)
Property: Authentication Data (SCRAM initial) (~80 bytes)
Client Identifier "device-007-abcd" (2 + 16 = 18 bytes)
Total encoded CONNECT: ~147 bytes
Even for CONNECT with modest authentication, 512+ bytes of headroom is reasonable.
Backward compatibility with MQTT 3.1.1
MQTT 3.1.1 has no Maximum Packet Size property. Compatibility considerations:
MQTT 3.1.1 client to MQTT 5 Server
The Server downgrades to MQTT 3.1.1 semantics. Maximum Packet Size cannot be negotiated. The Server uses an implementation-specific default limit for inbound packets. Clients using MQTT 3.1.1 have no way to know this limit — they just risk connection drops if they send too-large packets.
MQTT 5 client to MQTT 3.1.1 Server
The client’s CONNECT returns CONNACK with Reason Code 0x84 (Unsupported Protocol Version), forcing the client to reconnect with MQTT 3.1.1. Once using MQTT 3.1.1, no Maximum Packet Size negotiation is available.
Mixed-version fleets
Servers supporting both MQTT versions typically enforce a common packet size limit implementation-side, applying it to all clients regardless of protocol version. MQTT 5 clients get to see the limit via Maximum Packet Size; MQTT 3.1.1 clients don’t but are subject to the same underlying limit.
For broader version comparison, see our MQTT 5 vs MQTT 3.1.1 Comparison article.
Common errors and troubleshooting
| Symptom | Likely cause | What to check |
|---|---|---|
| DISCONNECT 0x95 on PUBLISH | Payload too large for Server’s Maximum Packet Size | Check Server’s declared limit; reduce payload or request higher limit |
| DISCONNECT 0x95 on CONNECT | CONNECT packet larger than implicit Server limit | Reduce Client Identifier, credentials, Will Message size |
| Sporadic 0x95 failures | Client not calculating packet size correctly | Include ALL properties in size calculation |
| 0x95 on small payloads | Heavy User Properties overhead | Reduce number/size of User Properties |
| Client crashes on inbound message | Client’s declared Maximum Packet Size too small OR client doesn’t respect its own limit | Check client-side memory allocation logic |
| Firmware update fails at chunk N | Chunk size exceeds Maximum Packet Size | Reduce chunk size or increase Maximum Packet Size |
| Some messages accepted, others rejected | Property overhead varies by message | Standardize property usage or use higher limit |
Frequently asked questions
What is MQTT 5 Maximum Packet Size?
Maximum Packet Size is a UINT32 property (0x27) that MQTT 5 parties exchange during connection setup to specify the largest packet in bytes they will accept. The client sends it in CONNECT (limiting the Server’s outbound packets); the Server sends it in CONNACK (limiting the client’s outbound packets). The limits are independent per direction. Exceeding the limit triggers DISCONNECT with Reason Code 0x95 (Packet too large).
What does Reason Code 0x95 mean?
Reason Code 0x95 means “Packet too large” — the receiver got a packet exceeding its declared Maximum Packet Size. It’s carried in DISCONNECT before the connection is closed. If your client sees 0x95 frequently, either your client isn’t tracking packet size correctly, your payloads are legitimately too large for the Server’s limit, or you’re forgetting to account for properties overhead in size calculations.
Does Maximum Packet Size include just the payload?
No. Maximum Packet Size limits the entire MQTT packet — Fixed Header, Variable Header, and Payload combined. Common overhead contributors include topic names (can be 30-60 bytes), User Properties (~50-200 bytes each), Response Topic, Correlation Data (up to 65,535 bytes), Content Type, and Authentication Data. A 100-byte payload with rich properties can easily be a 500+ byte packet.
What is the absolute maximum MQTT packet size?
The MQTT protocol’s absolute maximum is 268,435,455 bytes (~256 MB). This comes from the Variable Byte Integer encoding of the Remaining Length field, which supports up to 4 bytes of length. Adding the 1-byte Fixed Header, total packet size ceiling is 268,435,460 bytes. No MQTT 5 implementation can accept or send packets larger than this — Maximum Packet Size values above this are invalid.
What happens if Maximum Packet Size is not sent?
If the property is absent from CONNECT or CONNACK, no explicit per-connection limit applies. The sender is still bounded by the MQTT absolute maximum (~256 MB), but there’s no smaller limit declared. Implementations typically have internal default limits regardless — well-designed brokers declare Maximum Packet Size in CONNACK to make the limit explicit, even if it’s generous.
Can I set Maximum Packet Size to 0?
No. Setting Maximum Packet Size to 0 is invalid and triggers Protocol Error (Reason Code 0x82). The value must be at least 1. In practice, useful values are much higher — at minimum enough to fit CONNECT/CONNACK packets with basic properties (typically several hundred bytes).
How do I tune Maximum Packet Size for embedded devices?
For memory-constrained embedded devices, allocate roughly 25% of available RAM for the largest expected message. A device with 64 KB RAM might set Maximum Packet Size to 4-8 KB. A device with 512 KB RAM might use 16-64 KB. Balance against realistic payload needs — if you’ll never receive messages larger than 1 KB, don’t reserve 16 KB. Higher values use more memory but can accept larger messages without buffer overflows.
How do Flow Control and Maximum Packet Size work together?
They together bound receiver memory. Flow Control’s Receive Maximum limits how many in-flight QoS 1+2 messages exist simultaneously; Maximum Packet Size limits how big each one can be. Worst-case memory = Receive Maximum × Maximum Packet Size. For Receive Maximum = 100 and Maximum Packet Size = 1 MB, receivers must be prepared for ~100 MB of in-flight message data. Tune both together for your memory budget.
Do Topic Aliases help with Maximum Packet Size limits?
Yes. Topic Aliases replace repeated long topic name strings with 2-byte aliases, significantly reducing per-packet size after the first message for each topic. If your Maximum Packet Size is tight and you publish to topics with long names, Topic Aliases can effectively raise your payload capacity by reclaiming the bytes previously consumed by topic name repetition. Especially valuable on constrained networks.
Can Maximum Packet Size differ between client and Server?
Yes. Client and Server declare their own independent Maximum Packet Size values. The client’s value (in CONNECT) applies to Server→client packets. The Server’s value (in CONNACK) applies to client→Server packets. A constrained client might declare 4 KB while the Server accepts up to 1 MB — meaning the client sends large packets to the Server but can only receive small packets. This asymmetric configuration is common for gateways aggregating data from many devices.
What common properties contribute the most to packet size?
The heaviest contributors are typically: Authentication Data (up to several KB for complex tokens or SCRAM exchanges), Correlation Data (up to 65,535 bytes for structured correlation), User Properties (each name+value pair adds 5-200+ bytes, and many are often used), Topic Name (long hierarchical topics can be 40-80 bytes), Response Topic (adds another topic name), and Reason String (human-readable error descriptions in acknowledgements). For lean packets, minimize these — use Topic Aliases, short property names, and avoid unnecessary User Properties.
Does Maximum Packet Size apply to all MQTT packet types?
Yes. Maximum Packet Size applies to every packet type — PUBLISH, SUBSCRIBE, UNSUBSCRIBE, CONNECT, and all acknowledgement types (PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, UNSUBACK, DISCONNECT, AUTH). PUBLISH is the most common cause of hitting the limit because of variable payload sizes, but CONNECT can also hit the limit with heavy authentication or Will Message content. SUBSCRIBE with many topic filters can also be substantial.
Should MQTT clients calculate expected packet size before sending?
Yes, when close to limits. Well-behaved MQTT 5 clients should track the Server’s declared Maximum Packet Size and calculate expected packet size before serializing. For most typical messages this is overkill (packets are far below limits), but for large payloads or heavy properties usage, size calculation prevents surprise DISCONNECT 0x95 errors. The alternative — try sending and see if disconnected — creates unpredictable failures.
