MQTT (MQ Telemetry Transport) is the most widely used protocol in industrial IoT. It connects sensors, PLCs, gateways, and edge devices to cloud platforms and SCADA historians using a lightweight publish/subscribe model over TCP.
Wireshark fully decodes MQTT — every CONNECT, PUBLISH, SUBSCRIBE, and DISCONNECT message, including topic names, QoS levels, payloads, and client IDs — all in plain text.
MQTT runs on TCP port 1883 (unencrypted) and TCP port 8883 (TLS encrypted). Wireshark decodes port 1883 automatically. TLS traffic on port 8883 requires decryption keys.
This guide covers capture setup, verified display filters, how to read decoded MQTT packets, and how to diagnose the most common MQTT problems in industrial environments.
In This Guide
1. How MQTT Appears in Wireshark
Wireshark decodes MQTT as a single protocol layer on top of TCP:
| Layer | What It Shows |
|---|---|
| TCP | Port 1883 (or 8883 for TLS), TCP handshake, keep-alive |
| MQTT | Message type, topic, QoS, payload, client ID, return codes |
The Info column shows a summary like:
Connect Command
Connect Ack
Publish Message [sensor/temperature] (QoS 1, id=1)
Publish Ack (id=1)
Subscribe Request (id=2) [plant/alarms/#]
Subscribe Ack (id=2)
Disconnect Req
2. How to Capture MQTT Traffic
Capture Filter
tcp port 1883
For TLS-encrypted MQTT:
tcp port 8883
Where to Capture
| Location | When to Use |
|---|---|
| On the MQTT broker server | See all client connections, publishes, and subscriptions |
| On the IoT gateway | See what the gateway sends to the broker |
| On a network switch (port mirroring) | Non-intrusive capture between devices and broker |
| On the edge device | If you need to verify what the device actually sends |
3. Decoding MQTT on Non-Standard Ports
Wireshark decodes MQTT automatically on port 1883. Some brokers use custom ports (e.g., 1884, 8080, 9001 for WebSocket).
Fix: Use “Decode As”
- Right-click a packet on the non-standard port
- Select Decode As…
- Set: Field = TCP port, Value = your port, Current = MQTT
- Click OK
4. Display Filters for MQTT
All filter field names verified against the official Wireshark MQTT Display Filter Reference.
Basic Filters
| Filter | What It Shows |
|---|---|
mqtt | All MQTT traffic |
tcp.port == 1883 | All traffic on MQTT port |
tcp.port == 8883 | All MQTT-over-TLS traffic |
Message Type Filters
| Filter | What It Shows |
|---|---|
mqtt.msgtype == 1 | CONNECT |
mqtt.msgtype == 2 | CONNACK |
mqtt.msgtype == 3 | PUBLISH |
mqtt.msgtype == 4 | PUBACK (QoS 1 acknowledgment) |
mqtt.msgtype == 5 | PUBREC (QoS 2 step 1) |
mqtt.msgtype == 6 | PUBREL (QoS 2 step 2) |
mqtt.msgtype == 7 | PUBCOMP (QoS 2 step 3) |
mqtt.msgtype == 8 | SUBSCRIBE |
mqtt.msgtype == 9 | SUBACK |
mqtt.msgtype == 10 | UNSUBSCRIBE |
mqtt.msgtype == 11 | UNSUBACK |
mqtt.msgtype == 12 | PINGREQ |
mqtt.msgtype == 13 | PINGRESP |
mqtt.msgtype == 14 | DISCONNECT |
Connection Filters
| Filter | What It Shows |
|---|---|
mqtt.clientid | Client ID string |
mqtt.clientid == "gateway-01" | Traffic from a specific client |
mqtt.conack.val | CONNACK return code |
mqtt.conack.val == 0 | Successful connections only |
mqtt.conack.val != 0 | Failed connections only |
mqtt.conflag.cleansess | Clean Session flag |
mqtt.conflag.willflag | Will Flag (LWT configured) |
mqtt.conflag.uname | Username present |
mqtt.conflag.passwd | Password present |
mqtt.kalive | Keep-Alive interval value |
Publish and Subscribe Filters
| Filter | What It Shows |
|---|---|
mqtt.topic | Topic name in PUBLISH or SUBSCRIBE |
mqtt.topic contains "temperature" | Topics containing “temperature” |
mqtt.topic contains "alarm" | Topics containing “alarm” |
mqtt.msg | Message payload (raw bytes) |
mqtt.msg_text | Message payload (as text, Wireshark 3.2+) |
mqtt.msgid | Message Identifier (for QoS 1 and 2) |
mqtt.qos | QoS level (0, 1, or 2) |
mqtt.dupflag | Duplicate flag (retransmitted message) |
mqtt.retain | Retain flag |
MQTT v5.0 Filters
| Filter | What It Shows |
|---|---|
mqtt.connack.reason_code | CONNACK reason code (v5.0) |
mqtt.disconnect.reason_code | DISCONNECT reason code (v5.0) |
mqtt.properties | MQTT v5.0 properties |
mqtt.prop_key | User property key |
mqtt.prop_value | User property value |
Combination Examples
All PUBLISH messages on a specific topic:
mqtt.msgtype == 3 && mqtt.topic contains "sensor/temperature"
Failed connection attempts:
mqtt.conack.val != 0
All messages from a specific client:
mqtt.clientid == "plc-gateway-01"
QoS 1 messages that have not been acknowledged:
mqtt.msgtype == 3 && mqtt.qos == 1 && !mqtt.msgtype == 4
Keep-alive pings:
mqtt.msgtype == 12 || mqtt.msgtype == 13
5. How to Read a Decoded MQTT Packet
CONNECT Packet
MQ Telemetry Transport Protocol, Connect Command
Header Flags: 0x10 (Connect Command)
Msg Len: 45
Protocol Name: MQTT
Version: MQTT v3.1.1 (4)
Connect Flags: 0xc2 (User Name, Password, Clean Session)
Keep Alive: 60
Client ID: gateway-01
User Name: admin
Password: ****
PUBLISH Packet
MQ Telemetry Transport Protocol, Publish Message
Header Flags: 0x32 (Publish Message, QoS 1)
Msg Len: 38
Topic: plant/line1/temperature
Message Identifier: 1
Message: 23.5
CONNACK Packet
MQ Telemetry Transport Protocol, Connect Ack
Header Flags: 0x20 (Connect Ack)
Msg Len: 2
Acknowledge Flags: 0x00
Return Code: Connection Accepted (0)
6. What a Healthy MQTT Session Looks Like
| # | Direction | MQTT Info |
|---|---|---|
| 1 | Client → Broker | TCP SYN |
| 2 | Broker → Client | TCP SYN-ACK |
| 3 | Client → Broker | TCP ACK |
| 4 | Client → Broker | Connect Command (Client ID, credentials, keep-alive) |
| 5 | Broker → Client | Connect Ack (Return Code: 0 = Accepted) |
| 6 | Client → Broker | Subscribe Request [plant/line1/#] |
| 7 | Broker → Client | Subscribe Ack |
| 8 | Client → Broker | Publish Message [plant/line1/temperature] QoS 1 |
| 9 | Broker → Client | Publish Ack (id matches) |
| … | … | Publish/Ack cycle repeats |
| N | Client → Broker | Ping Request (keep-alive) |
| N+1 | Broker → Client | Ping Response |
Key signs of healthy communication:
- CONNACK return code = 0 (Connection Accepted)
- Every QoS 1 PUBLISH has a matching PUBACK
- PINGREQ/PINGRESP pairs appear at the keep-alive interval
- No TCP retransmissions
- No DISCONNECT with error reason codes
7. Diagnosing Connection Failures
Filter: mqtt.msgtype == 2 (CONNACK packets)
CONNACK Return Codes (MQTT v3.1.1)
| Code | Meaning | Common Cause |
|---|---|---|
| 0 | Connection Accepted | OK |
| 1 | Unacceptable Protocol Version | Client uses MQTT v3.1 but broker requires v3.1.1 or v5.0 |
| 2 | Identifier Rejected | Client ID is empty, too long, or contains invalid characters |
| 3 | Server Unavailable | Broker is starting up or overloaded |
| 4 | Bad User Name or Password | Wrong credentials |
| 5 | Not Authorized | Client not allowed to connect (ACL rejection) |
Filter for failures only: mqtt.conack.val != 0
No CONNACK at All
If the client sends CONNECT but receives no CONNACK:
| What You See | Cause | Fix |
|---|---|---|
| TCP SYN, no SYN-ACK | Broker is down or port 1883 is blocked | Check broker status. Check firewall. |
| TCP connected, CONNECT sent, no CONNACK | Broker received the CONNECT but did not respond | Broker overloaded, or CONNECT packet malformed. Check broker logs. |
| TCP RST after CONNECT | Broker rejected the TCP connection | Max connections exceeded. Check broker configuration. |
8. Diagnosing Publish and Subscribe Problems
No Messages Received After Subscribing
Filter: mqtt.msgtype == 8 || mqtt.msgtype == 9 (SUBSCRIBE + SUBACK)
| What to Check | What It Means |
|---|---|
| SUBSCRIBE packet shows the correct topic filter | If the topic is wrong, no messages match |
| SUBACK return code = 0, 1, or 2 | Subscription accepted at QoS 0, 1, or 2 |
| SUBACK return code = 128 | Subscription rejected — broker denied the subscription |
If SUBACK shows success but no PUBLISH messages arrive, the problem is on the publisher side — no device is publishing to that topic.
Message Payload is Empty or Wrong
Filter: mqtt.msgtype == 3 && mqtt.topic contains "your/topic"
Click the PUBLISH packet and check:
mqtt.msg— raw payload bytesmqtt.msg_text— payload as readable text (Wireshark 3.2+)
If the payload is JSON, Wireshark shows it as text. If it is binary (Sparkplug B protobuf, for example), you will see raw bytes.
9. Diagnosing QoS and Message Delivery Issues
MQTT has three QoS levels:
| QoS | Delivery | Wireshark Packet Sequence |
|---|---|---|
| 0 | At most once (fire and forget) | PUBLISH only — no ACK |
| 1 | At least once (acknowledged) | PUBLISH → PUBACK |
| 2 | Exactly once (four-step handshake) | PUBLISH → PUBREC → PUBREL → PUBCOMP |
Diagnosing QoS 1 Problems
Filter: mqtt.msgtype == 3 && mqtt.qos == 1
Then check: does every PUBLISH have a matching PUBACK with the same mqtt.msgid?
If PUBACK is missing, the broker did not acknowledge the message. The client will retransmit with the DUP flag set.
Filter for retransmitted messages: mqtt.dupflag == 1
Diagnosing QoS 2 Problems
Filter: mqtt.msgtype == 5 || mqtt.msgtype == 6 || mqtt.msgtype == 7
The four-step handshake must complete fully. If any step is missing, the message delivery is incomplete.
10. Diagnosing Keep-Alive and Disconnect Problems
Keep-Alive
The client sends a PINGREQ at the keep-alive interval. The broker responds with PINGRESP. If the broker does not receive any packet within 1.5× the keep-alive time, it closes the connection.
Filter: mqtt.msgtype == 12 || mqtt.msgtype == 13
| Problem | What You See | Fix |
|---|---|---|
| Client disconnects unexpectedly | No PINGREQ sent before broker times out | Client is too busy or network latency is too high. Increase keep-alive. |
| Broker drops the connection | TCP RST or FIN after a period of no MQTT packets | Client keep-alive is too long. Reduce it, or ensure PINGREQ is sent on time. |
To see the configured keep-alive: mqtt.kalive in the CONNECT packet.
Unclean Disconnects
A clean disconnect shows DISCONNECT (msgtype 14) followed by TCP FIN. If the client crashes or loses network, you see only TCP RST or timeout — no DISCONNECT packet. The broker then publishes the Last Will message (if configured).
11. Diagnosing Last Will and Testament (LWT)
The LWT is configured in the CONNECT packet. If the client disconnects without sending DISCONNECT, the broker publishes the Will message.
Filter: mqtt.conflag.willflag == 1
Check the CONNECT packet for:
mqtt.conflag.willflag— Will Flag is setmqtt.willtopic— the topic the Will message will be published tomqtt.willmsg— the Will message payloadmqtt.conflag.qos— QoS level for the Will messagemqtt.conflag.retain— whether the Will message is retained
If LWT is not working, verify that the Will Flag is set in the CONNECT packet. If it is missing, the client is not configuring LWT.
12. MQTT over TLS (Port 8883)
MQTT over TLS on port 8883 is encrypted. Wireshark shows it as TLS, not MQTT.
To decode it:
Option 1: Capture Before Encryption
If you have access to the MQTT client or broker, capture on the loopback interface (localhost) where traffic is unencrypted.
Option 2: Use Pre-Master Secret Log
If the client supports it (e.g., Python paho-mqtt with SSL), export the TLS pre-master secret key log and load it in Wireshark:
- Set the environment variable:
SSLKEYLOGFILE=/path/to/keylog.txt - In Wireshark: Edit → Preferences → Protocols → TLS → (Pre)-Master-Secret log filename → point to the keylog file
- Wireshark now decrypts TLS and shows MQTT inside
Option 3: Disable TLS for Debugging
Temporarily switch the broker to port 1883 (unencrypted) for troubleshooting. Do not leave this in production.
13. MQTT in Industrial Systems: Sparkplug B
Sparkplug B is an industrial MQTT specification by the Eclipse Foundation. It standardizes:
- Topic namespace:
spBv1.0/GroupID/MSGTYPE/EdgeNodeID/DeviceID - Message types: NBIRTH, NDEATH, DBIRTH, DDEATH, NDATA, DDATA, NCMD, DCMD
- Payload encoding: Protocol Buffers (protobuf) — binary, not JSON
In Wireshark, Sparkplug B PUBLISH messages show:
- Topic:
spBv1.0/Plant1/DDATA/Gateway01/PLC1 - Payload: binary protobuf (not human-readable without decoding)
Filter for Sparkplug topics:
mqtt.topic contains "spBv1.0"
Filter for specific message types:
mqtt.topic contains "NBIRTH"
mqtt.topic contains "DDATA"
mqtt.topic contains "DCMD"
To decode the protobuf payload, export the raw bytes from Wireshark and use a Sparkplug B decoder tool or a protobuf parser.
14. Useful Wireshark Columns for MQTT Analysis
| Column Title | Type | Field Name |
|---|---|---|
| Message Type | Custom | mqtt.msgtype |
| Topic | Custom | mqtt.topic |
| Client ID | Custom | mqtt.clientid |
| QoS | Custom | mqtt.qos |
| Message ID | Custom | mqtt.msgid |
| Payload (text) | Custom | mqtt.msg_text |
| Delta Time | Custom | frame.time_delta_displayed |
15. Common MQTT Problems and What They Look Like in Wireshark
| Problem | Wireshark Symptom | Filter |
|---|---|---|
| Broker offline | TCP SYN with no SYN-ACK | tcp.flags.syn == 1 && tcp.port == 1883 |
| Bad credentials | CONNACK return code 4 or 5 | mqtt.conack.val == 4 || mqtt.conack.val == 5 |
| Client ID rejected | CONNACK return code 2 | mqtt.conack.val == 2 |
| Subscription denied | SUBACK return code 128 | mqtt.suback.return_code == 128 |
| Messages not delivered | PUBLISH sent but no PUBACK | mqtt.msgtype == 3 && mqtt.qos == 1 — then check for matching PUBACK |
| Duplicate messages | DUP flag set on PUBLISH | mqtt.dupflag == 1 |
| Client drops unexpectedly | No DISCONNECT before TCP RST/FIN | mqtt.msgtype == 14 — if absent before TCP close |
| Keep-alive timeout | No PINGREQ within keep-alive interval | mqtt.msgtype == 12 — check timing vs mqtt.kalive |
| LWT not configured | CONNECT without Will Flag | mqtt.conflag.willflag == 0 |
| Payload encoding wrong | Payload shows binary instead of text | Check mqtt.msg vs mqtt.msg_text — may be protobuf or binary format |
Summary
Wireshark decodes every MQTT message type — CONNECT, PUBLISH, SUBSCRIBE, PINGREQ, DISCONNECT, and all QoS handshake packets.
The key things to remember:
- MQTT runs on TCP port 1883 (unencrypted) and 8883 (TLS)
- Use
mqttas the main display filter - Use
mqtt.msgtypeto filter by message type (3=PUBLISH, 1=CONNECT, 12=PINGREQ) - Use
mqtt.topic contains "keyword"to filter by topic name - Use
mqtt.conack.val != 0to find failed connections - Use
mqtt.dupflag == 1to find retransmitted messages - Use
mqtt.conflag.willflagto check if LWT is configured - For TLS traffic on port 8883, load the pre-master secret key log to decrypt
- For Sparkplug B, filter with
mqtt.topic contains "spBv1.0"— payload is protobuf
