MQTT Fundamentals

MQTT 5 User Properties: Adding Metadata Without Touching the Payload

MQTT 5 user properties are key/value headers you can attach to a message without changing its payload. Learn how they work, what they are good for, and how to send and read them in Python, Node.js and the CLI.

Message envelope with key value labels attached, illustrating MQTT user properties in MQTT 5
Run this on your own MQTT brokerGet a private, managed MQTT broker on the Free plan and start from ready-made code samples.
Try it free Code samples No card required

MQTT user properties are key/value string pairs that you attach to an MQTT 5 message, much like HTTP headers. They let you send metadata such as a trace ID, a schema version or the unit of a measurement without changing the payload format your consumers already parse. In this article you will learn how user properties behave, where they genuinely help, and how to publish and read them in Python, Node.js and the command line.

What MQTT User Properties Are

MQTT 3.1.1 had no place for metadata. Anything beyond the topic and the payload had to be squeezed into the payload itself or encoded in the topic name. MQTT 5 introduced properties on most packets, and the User Property is the one left open for applications.

Key facts about user properties:

  • Each property is a pair of UTF-8 strings: a name and a value.
  • A message can carry any number of them, and the same name may appear more than once.
  • Order is preserved. The broker forwards them in the order they were sent.
  • On PUBLISH packets, the broker passes them to subscribers unchanged.
  • They count toward the packet size, and therefore toward any message size limit.
  • They can also appear on CONNECT, SUBSCRIBE, DISCONNECT and other packets, though PUBLISH is where applications use them most.

Because the broker does not interpret them, user properties are purely a contract between your publishers and subscribers. They are one of several MQTT 5 features that make the protocol friendlier for larger systems.

Common Use Cases for MQTT User Properties

Use case Example property Why not in the payload
Distributed tracing traceparent=00-4bf9...-01 Tracing is cross-cutting; payload schemas should not change for it
Schema versioning schema=telemetry.v3 Consumers pick a parser before decoding
Content metadata unit=celsius, encoding=gzip Describes how to read the payload
Source identification source=gateway-12, fw=2.4.1 Useful for debugging without touching data
Routing hints tenant=plant-a Lets middleware filter without parsing
Test markers synthetic=true Lets consumers drop test traffic

The common thread: information about the message, not the measurement itself. If a dashboard needs a value to draw a chart, it belongs in the payload. If a value tells software how to handle the message, a user property is a good fit.

For binary payloads such as Protobuf, user properties are especially handy because you can add metadata without changing the .proto file. See MQTT payload formats for that comparison.

Sending User Properties from the Command Line

The Mosquitto clients support user properties when you select MQTT 5. We will use the public MQTT test broker at broker.connectmq.com with a unique topic prefix.

Subscribe first. The %P format specifier prints user properties:

mosquitto_sub -V mqttv5 -h broker.connectmq.com -p 1883 -i up-sub-w2c5 \
  -t 'connectmq/demo/w2c5/#' -F '%t %p | props: %P'

Then publish with two properties:

mosquitto_pub -V mqttv5 -h broker.connectmq.com -p 1883 -i up-pub-w2c5 \
  -t connectmq/demo/w2c5/line1/temp -m '21.7' \
  -D publish user-property unit celsius \
  -D publish user-property schema temp.v1

The subscriber prints the topic, payload and unit:celsius schema:temp.v1. Publish the same command with -V mqttv311 and the subscriber still gets the payload, but there are no properties, because MQTT 3.1.1 cannot carry them.

Publishing and Reading User Properties in Python

In paho-mqtt 2.x, user properties are a list of tuples on a Properties object. A list rather than a dict, because names may repeat:

import time
import uuid
import paho.mqtt.client as mqtt
from paho.mqtt.properties import Properties
from paho.mqtt.packettypes import PacketTypes

BROKER = "broker.connectmq.com"
TOPIC = "connectmq/demo/w2c5/line1/pressure"

def on_connect(client, userdata, flags, reason_code, properties):
    client.subscribe(TOPIC, qos=1)

def on_subscribe(client, userdata, mid, reason_code_list, properties):
    props = Properties(PacketTypes.PUBLISH)
    props.UserProperty = [
        ("trace-id", uuid.uuid4().hex),
        ("unit", "bar"),
        ("source", "gateway-12"),
    ]
    client.publish(TOPIC, "2.31", qos=1, properties=props)

def on_message(client, userdata, msg):
    user_props = dict(getattr(msg.properties, "UserProperty", []))
    print(msg.topic, msg.payload.decode(), user_props)

client = mqtt.Client(
    mqtt.CallbackAPIVersion.VERSION2,
    client_id=f"up-py-{uuid.uuid4().hex[:8]}",
    protocol=mqtt.MQTTv5,
)
client.on_connect = on_connect
client.on_subscribe = on_subscribe
client.on_message = on_message
client.connect(BROKER, 1883, keepalive=30)
client.loop_start()
time.sleep(3)
client.loop_stop()
client.disconnect()

Converting to a dict is convenient, but only if you know your names are unique. If you allow repeated names, keep the list.

User Properties in Node.js

MQTT.js v5 exposes user properties as a plain object in properties.userProperties. A value may be a string or an array of strings for repeated names:

const mqtt = require("mqtt");

const client = mqtt.connect("mqtts://broker.connectmq.com:8883", {
  clientId: "up-js-" + Math.random().toString(16).slice(2, 10),
  protocolVersion: 5,
});
const topic = "connectmq/demo/w2c5/line1/flow";

client.on("connect", () => {
  client.subscribe(topic, { qos: 1 }, () => {
    client.publish(topic, "12.4", {
      qos: 1,
      properties: {
        userProperties: { unit: "m3/h", tag: ["calibrated", "line1"] },
      },
    });
  });
});

client.on("message", (t, payload, packet) => {
  console.log(t, payload.toString(), packet.properties?.userProperties);
  client.end();
});

User Properties vs Topic Levels vs Payload Fields

There are three places to put a piece of information in an MQTT message, and each has a different cost:

Location Good for Visible to Cost
Topic level Routing and subscription filtering (site, line, device) Broker, every subscriber, wildcards Part of every topic string; changes break subscriptions
User property Handling instructions, tracing, schema version Subscribers only; broker ignores it Bytes per message; lost on MQTT 3.1.1 hops
Payload field Business data that consumers store or display Subscribers after decoding Requires schema changes to add

A useful rule of thumb: if a subscriber needs to filter on it, use the topic. If a subscriber needs to decide how to process the message before decoding, use a user property. If the subscriber stores or displays it, put it in the payload.

For example, a unit value could go in either a property or the payload. If every consumer stores the unit alongside the value, put it in the payload so it survives being written to a database. If the unit is fixed per topic and only a validation layer checks it, a property is cleaner. Note that wildcard subscriptions only filter on topic levels: a subscriber cannot ask the broker for "messages where tenant equals plant-a" based on a user property.

Design Guidelines

  1. Define a small vocabulary. Agree on a handful of names across teams and document them. Ad hoc properties quickly become noise.
  2. Keep them short. Properties travel with every message. Ten long headers on a 20-byte sensor reading can multiply its size.
  3. Do not put secrets in them. They are as visible as the payload to anyone who can subscribe.
  4. Do not depend on them for critical data if some consumers still use MQTT 3.1.1. Those consumers will silently lose them.
  5. Use standard formats where they exist, such as W3C traceparent for tracing, so tools can understand them.
  6. Prefer content type for the payload type. MQTT 5 has a dedicated Content Type property and a Payload Format Indicator; use those instead of a custom content-type user property.

Troubleshooting User Properties

Properties are missing on the subscriber. The subscriber or publisher connected with MQTT 3.1.1. Both connections must be MQTT 5.

Messages are rejected as too large. Properties count toward the packet size. On a size-limited plan or broker, trim them. Our article on the MQTT message size limit explains how the limit is enforced.

Values appear as arrays unexpectedly. In MQTT.js a repeated name becomes an array. Handle both shapes or avoid repeating names.

Bridged or republished messages lose properties. Any component that reads and republishes a message must copy the properties explicitly; many simple scripts do not.

User Properties on ConnectMQ

ConnectMQ forwards MQTT 5 user properties unchanged between publishers and subscribers, like any standards-compliant broker. Remember that they count toward the message size: 256 KB on the free Serverless plan, 1 MB on the paid Serverless plan, and 64 KB on the public test broker. To see your metadata in practice, subscribe with the Mosquitto command above or with your own client against your broker, using the credentials from the portal. If you are still choosing infrastructure, our overview of a fully managed MQTT service explains what you get, and the MQTT tutorial covers the fundamentals.

Get Started with ConnectMQ

Sign up at app.connectmq.com and create a broker on the free Serverless plan, no credit card required. Connect with MQTT 5, add a trace ID to your messages, and follow them from device to dashboard on your own cloud MQTT broker.

Frequently Asked Questions

Can user property names repeat?

Yes. MQTT 5 allows the same name more than once, and the broker preserves the order. Store them as a list if you rely on that.

Does the broker change user properties?

On PUBLISH packets the broker forwards them unchanged to subscribers. It does not interpret or filter them.

Are user properties available in MQTT 3.1.1?

No. They are an MQTT 5 feature. A 3.1.1 subscriber receives the payload without any properties.

Do user properties count toward message size?

Yes. They are part of the packet, so they count toward the broker's maximum packet size.

Related articles

Why Your MQTT Client Keeps Disconnecting (and How to Fix It)

Reconnect loops are one of the most common MQTT problems. Learn the five usual causes, from duplicate client IDs to keep alive timeouts and oversized packets, and follow a diagnosis flow that pinpoints yours.

8 min read

Who Invented MQTT? The History from Oil Pipelines to IoT

MQTT was invented in 1999 by Andy Stanford-Clark of IBM and Arlen Nipper of Arcom to monitor oil pipelines over satellite. Follow its path to open source, OASIS, ISO and today's managed cloud brokers.

7 min read

MQTT Maximum Message Size: Limits and Large Payload Strategies

MQTT can technically carry a 256 MB packet, but brokers, client libraries and devices impose far lower limits. Learn where the real limits are, what happens when you exceed them, and how to send large payloads.

7 min read

All articles →

Your private MQTT broker is a few clicks away

Create an account, get your broker credentials and connect your first device — no servers to install, patch or scale.