MQTT control packets are the 15 message types that clients and brokers exchange, from CONNECT to AUTH, and every MQTT interaction is built from them. This reference explains what each packet does, who sends it, how to recognise it by its first byte, and what it looks like in client debug logs, so you can follow a conversation with a broker and pinpoint where it goes wrong.
MQTT Control Packets at a Glance
The packet type lives in the top four bits of the first byte of every packet. MQTT 3.1.1 defines types 1 to 14; MQTT 5 adds type 15, AUTH. Type 0 is reserved and never valid.
| Type | Name | Direction | First byte | Purpose |
|---|---|---|---|---|
| 1 | CONNECT | Client to broker | 0x10 | Open a session |
| 2 | CONNACK | Broker to client | 0x20 | Accept or refuse the connection |
| 3 | PUBLISH | Both | 0x30-0x3F | Deliver an application message |
| 4 | PUBACK | Both | 0x40 | Acknowledge a QoS 1 publish |
| 5 | PUBREC | Both | 0x50 | QoS 2 step 1: publish received |
| 6 | PUBREL | Both | 0x62 | QoS 2 step 2: release |
| 7 | PUBCOMP | Both | 0x70 | QoS 2 step 3: complete |
| 8 | SUBSCRIBE | Client to broker | 0x82 | Request subscriptions |
| 9 | SUBACK | Broker to client | 0x90 | Grant or refuse subscriptions |
| 10 | UNSUBSCRIBE | Client to broker | 0xA2 | Remove subscriptions |
| 11 | UNSUBACK | Broker to client | 0xB0 | Confirm unsubscribe |
| 12 | PINGREQ | Client to broker | 0xC0 | Keep alive probe |
| 13 | PINGRESP | Broker to client | 0xD0 | Keep alive answer |
| 14 | DISCONNECT | Client to broker (both in MQTT 5) | 0xE0 | Close the connection cleanly |
| 15 | AUTH | Both (MQTT 5 only) | 0xF0 | Enhanced authentication exchange |
Knowing the first byte is handy when you read a raw hex dump: 0xC0 0x00 is a PINGREQ, 0x82 starts a SUBSCRIBE. The byte-level layout of headers and payloads is covered in the guide to MQTT packet structure.
Watching Control Packets in a Debug Log
The easiest way to see these packets is the -d flag of the Mosquitto clients. Against the public MQTT test broker:
mosquitto_sub -h broker.connectmq.com -p 1883 -i pkt-demo-e41f \
-t "connectmq/demo/e41f/#" -q 1 -k 10 -d
In a second terminal, publish a message:
mosquitto_pub -h broker.connectmq.com -p 1883 -i pkt-pub-e41f \
-t "connectmq/demo/e41f/temp" -m "22.1" -q 1 -d
The subscriber's output will look similar to this:
Client pkt-demo-e41f sending CONNECT
Client pkt-demo-e41f received CONNACK (0)
Client pkt-demo-e41f sending SUBSCRIBE (Mid: 1, Topic: connectmq/demo/e41f/#, QoS: 1, Options: 0x00)
Client pkt-demo-e41f received SUBACK
Subscribed (mid: 1): 1
Client pkt-demo-e41f received PUBLISH (d0, q1, r0, m1, 'connectmq/demo/e41f/temp', ... (4 bytes))
Client pkt-demo-e41f sending PUBACK (m1, rc0)
22.1
Client pkt-demo-e41f sending PINGREQ
Client pkt-demo-e41f received PINGRESP
The -k 10 keep alive makes PINGREQ appear after ten idle seconds. In the PUBLISH line, d0, q1, r0, m1 means DUP 0, QoS 1, retain 0 and message ID 1. Paho Python prints similar lines with client.enable_logger(), and MQTT.js does so with the DEBUG=mqttjs* environment variable.
Connection Packets: CONNECT, CONNACK, DISCONNECT
CONNECT is always the first packet a client sends. It carries the protocol version, clean session or clean start flag, keep alive, client ID, optional Last Will and optional username and password. In MQTT 5 it also carries properties such as Session Expiry Interval, Receive Maximum and Topic Alias Maximum. Sending a second CONNECT on the same connection is a protocol error.
CONNACK is the broker's answer. It contains the Session Present flag and a return code (3.1.1) or reason code (MQTT 5). MQTT 5 brokers also use CONNACK properties to tell the client about their limits: Maximum QoS, Maximum Packet Size, Topic Alias Maximum, whether retain, wildcards or shared subscriptions are available, and an assigned client ID if the client sent an empty one. When a connection fails, CONNACK is the first place to look; see MQTT CONNACK return codes.
DISCONNECT closes the connection cleanly. A client that sends DISCONNECT before closing the socket suppresses its Last Will message (except in MQTT 5 with reason code 0x04). In MQTT 3.1.1 only the client sends DISCONNECT; in MQTT 5 the broker can send one too, with a reason code explaining why, such as 0x8E Session taken over or 0x97 Quota exceeded. If you see a reconnect loop, look for broker-sent DISCONNECTs; the guide on why an MQTT client keeps disconnecting walks through the causes.
Publishing Packets: PUBLISH and the Acknowledgements
PUBLISH carries the application message in either direction: client to broker when you publish, broker to client when you receive. Its fixed header flags hold DUP, QoS and RETAIN.
The acknowledgements depend on QoS, explained fully in MQTT QoS levels:
| QoS | Packet flow | Total packets per message |
|---|---|---|
| 0 | PUBLISH | 1 |
| 1 | PUBLISH, PUBACK | 2 |
| 2 | PUBLISH, PUBREC, PUBREL, PUBCOMP | 4 |
- PUBACK confirms a QoS 1 message. Until it arrives, the sender keeps the message and may resend it with DUP set after reconnecting.
- PUBREC is the receiver's "I have it" for QoS 2. The sender can now discard the message payload.
- PUBREL tells the receiver it may release the message to subscribers, and that the packet ID can be reused soon.
- PUBCOMP closes the QoS 2 exchange.
Note that each hop is independent. A QoS 1 publish from a sensor to the broker and a QoS 2 delivery from the broker to a subscriber are separate exchanges. In MQTT 5 these acknowledgements carry reason codes, so PUBACK can say 0x10 No matching subscribers or 0x87 Not authorized.
Subscription Packets: SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK
SUBSCRIBE contains a packet identifier and one or more topic filters, each with a requested QoS. MQTT 5 adds subscription options such as No Local, Retain As Published and Retain Handling, plus an optional Subscription Identifier.
SUBACK returns one code per filter in the same order. Read every entry: a single SUBSCRIBE with three filters can succeed for two and fail for one. In 3.1.1 the failure value is 0x80; MQTT 5 gives specific reasons such as 0x8F Topic Filter invalid or 0x9E Shared Subscriptions not supported.
UNSUBSCRIBE and UNSUBACK mirror this for removal. MQTT 5 adds per-filter reason codes to UNSUBACK, including 0x11 No subscription existed.
Keep Alive Packets: PINGREQ and PINGRESP
PINGREQ and PINGRESP are two-byte packets that prove the connection is alive when no other traffic flows. The client sends PINGREQ if it has sent nothing within the keep alive interval; the broker must answer with PINGRESP. If the broker hears nothing for 1.5 times the keep alive, it closes the connection. If the client does not get PINGRESP in reasonable time, it should treat the connection as dead. The MQTT keep alive interval guide explains how to choose the value.
AUTH: The MQTT 5 Addition
AUTH supports enhanced authentication, such as challenge-response mechanisms, through the Authentication Method and Authentication Data properties. It is used after CONNECT for multi-step exchanges and can also start re-authentication on an existing connection. Most deployments, including username and password logins, never send AUTH, so seeing it in a capture is unusual unless you configured an enhanced method.
A Debugging Checklist by Packet
- No CONNACK? The problem is network, port or TLS, not MQTT.
- CONNACK with a non-zero code? Credentials, client ID or protocol version.
- SUBACK with 0x80 or higher? Bad filter, permission or unsupported feature.
- PUBLISH sent but no PUBACK? Check QoS support and connection state.
- Repeated PINGREQ without PINGRESP? The connection is half-dead; reconnect.
- Unexpected DISCONNECT from the broker? Read its reason code.
See Every Packet on ConnectMQ
ConnectMQ supports MQTT 3.1.1 and 5.0, so you can watch both the classic packet flow and MQTT 5 reason codes and properties against the same broker. For the bigger picture, read what an MQTT broker does with these packets, or follow the complete MQTT tutorial.
Build on ConnectMQ
When you move from experiments to devices, a hosted MQTT broker takes care of TLS, credentials and uptime. Create a free Serverless broker at app.connectmq.com with no credit card, then use the portal's in-browser client and live topic explorer while you debug. You can also compare options in our guide to the cloud MQTT broker market.
Frequently Asked Questions
How many MQTT control packet types are there?
MQTT 5 defines 15 control packet types, numbered 1 to 15. MQTT 3.1.1 has 14, because the AUTH packet was added in MQTT 5.
Which packets does a QoS 2 message use?
PUBLISH, PUBREC, PUBREL and PUBCOMP. The four-step handshake ensures the message is delivered exactly once between that sender and receiver.
Can the broker send a DISCONNECT packet?
Only in MQTT 5. In MQTT 3.1.1 the broker simply closes the network connection; MQTT 5 lets it send DISCONNECT with a reason code first.
Why do I see PINGREQ in my logs?
PINGREQ is the client's keep alive probe, sent when it has been idle for the keep alive interval. Regular PINGREQ and PINGRESP pairs are normal and show the connection is healthy.



