Sim, o curl fala MQTT. Desde a versão 7.70.0 o curl entende URLs mqtt://: um GET (o padrão) assina o topic que está no caminho da URL, e enviar dados com -d publica nele. Se o curl já está no seu servidor, runner de CI ou container, você consegue verificar se um broker aceita conexões e roteia uma mensagem sem instalar mais nada.
# Assinar (bloqueia até você apertar Ctrl+C)
curl mqtt://broker.connectmq.com/testmqtt/curl/demo
# Publicar (em um segundo terminal)
curl -d "hello from curl" mqtt://broker.connectmq.com/testmqtt/curl/demoEste guia mostra como esses comandos se comportam, o formato exato da saída, as limitações que importam e quando vale trocar por um cliente MQTT de verdade.
Verifique se o seu curl tem MQTT
MQTT é um recurso definido na compilação. Procure mqtt na lista de protocolos:
curl -V | grep -i protocols
# Protocols: dict file ftp ftps gopher ... mqtt pop3 pop3s rtsp ...Se mqtt não aparecer, o curl responde a uma URL mqtt:// com curl: (1) Protocol "mqtt" not supported (builds antigos acrescentam "or disabled in libcurl"). As versões atuais do macOS, a maioria das distribuições Linux e a imagem Docker oficial curlimages/curl incluem o suporte; um build mínimo pode não incluir.
docker run --rm curlimages/curl -d "hi" mqtt://broker.connectmq.com/testmqtt/curl/demoComo o curl mapeia MQTT em URLs
| Ação do curl | Comportamento MQTT |
|---|---|
curl mqtt://host/a/b | CONNECT, SUBSCRIBE em a/b, imprime cada mensagem até ser interrompido |
curl -d data mqtt://host/a/b | CONNECT, PUBLISH de data em a/b (QoS 0), DISCONNECT |
mqtt://host:1884/... | Porta fora do padrão (o padrão é 1883) |
-u user:pass | Usuário e senha no pacote CONNECT |
--max-time 10 | Encerra uma assinatura após 10 segundos |
O topic é o caminho da URL sem a barra inicial, então mqtt://broker.connectmq.com/testmqtt/curl/demo significa o topic testmqtt/curl/demo. O curl gera um client ID aleatório que começa com curl (por exemplo curlMsFB2wMW, visível com -v) e usa MQTT 3.1.1.
Publicar mensagens
-d recebe uma string, ou @arquivo para ler o payload de um arquivo:
# Texto simples
curl -d "21.5" mqtt://broker.connectmq.com/testmqtt/curl/sensor/temp
# JSON a partir do shell
curl -d '{"temp":21.5,"unit":"C"}' mqtt://broker.connectmq.com/testmqtt/curl/sensor/json
# Payload de um arquivo, com os bytes exatamente como estão
curl --data-binary @reading.json mqtt://broker.connectmq.com/testmqtt/curl/sensor/json
# Com credenciais (usuário e senha do seu broker)
curl -u "$MQTT_USER:$MQTT_PASS" -d "on" mqtt://broker.connectmq.com/testmqtt/curl/relay/1Uma publicação bem-sucedida não imprime nada e sai com status 0, o que torna o curl prático em scripts e health checks. Lembre que o código 0 só significa que o broker aceitou a conexão e que o curl enviou o PUBLISH: com QoS 0 não há confirmação, então isso não prova que alguém recebeu a mensagem.
Assinar e ler a saída
Uma assinatura roda até você apertar Ctrl+C ou até um limite de tempo encerrá-la:
curl -N --max-time 30 mqtt://broker.connectmq.com/testmqtt/curl/sensor/temp-N (--no-buffer) faz o curl escrever cada mensagem na hora, em vez de acumular a saída quando você a redireciona para outro programa. Quando o limite de tempo é atingido, o curl sai com código 28 (operation timed out). Esse é o jeito normal de uma assinatura com limite terminar, não um erro.
Curingas precisam ser codificados na URL
+ pode ser escrito como está, mas # inicia um fragmento de URL e é cortado antes de o curl enviar qualquer coisa. Codifique como %23:
curl -N 'mqtt://broker.connectmq.com/testmqtt/curl/%23' # testmqtt/curl/#
curl -N 'mqtt://broker.connectmq.com/testmqtt/curl/+/temp' # um nívelNão sabe o que um filtro abrange? Teste no topic matcher ou leia topics e curingas no MQTT.
O formato da saída
O curl não imprime mensagens como linhas de texto. Para cada PUBLISH recebido, ele escreve:
- dois bytes: o tamanho do topic (big-endian),
- o nome do topic,
- o payload, sem separador nem quebra de linha no final.
Esta é uma captura real de uma mensagem em uma assinatura com curinga:
00000000: 0011 7465 7374 6d71 7474 2f63 7572 6c2f ..testmqtt/curl/
00000010: 612f 627b 2274 223a 3231 2e35 7d a/b{"t":21.5}0x0011 é 17, o tamanho do topic testmqtt/curl/a/b, e o payload JSON vem logo em seguida. Isso basta para saber "chegou alguma coisa?", mas é chato de analisar quando várias mensagens chegam juntas. Um pouco de Python consegue separar o fluxo:
import sys
# Separa o fluxo do curl em pares topic/payload.
# Funciona quando os payloads são pequenos e chegam um por leitura; o curl não informa o tamanho do payload.
buf = sys.stdin.buffer
while True:
head = buf.read(2)
if len(head) < 2:
break
n = int.from_bytes(head, "big")
topic = buf.read(n).decode()
payload = buf.read1(65536) # o que chegou junto com esta mensagem
print(f"{topic} {payload.decode(errors='replace')}", flush=True)Como o curl não escreve o tamanho do payload, nenhum parser é totalmente confiável. Se você precisa processar mensagens em um programa, só isso já é motivo para usar mosquitto_sub -v ou uma biblioteca.
Limitações que você precisa conhecer
| Recurso | curl | mosquitto_pub / mosquitto_sub |
|---|---|---|
| Versão do MQTT | 3.1.1 | 3.1, 3.1.1, 5 |
| QoS na publicação | Só 0 | 0, 1, 2 |
| Publicação retida | Não | -r |
TLS (mqtts, porta 8883) | Não | --cafile, -p 8883 |
| WebSockets | Não (ws:// no curl é um WebSocket comum, não MQTT) | Não (use um cliente no navegador) |
| Client ID próprio, keep-alive, Last Will | Não | Sim |
| Saída de assinatura legível | Formato binário | -v imprime linhas topic payload |
A falta de TLS é a mais importante. O manual do curl diz isso diretamente: "MQTT over TLS is not supported (yet)." Nunca envie credenciais de produção com -u pela porta 1883 sem criptografia em uma rede não confiável: elas trafegam em texto puro. Para qualquer coisa além de um smoke test contra um listener TCP sem TLS, use mosquitto_pub e mosquitto_sub ou o MQTT CLI, que suportam TLS, MQTT 5 e todos os níveis de QoS.
Erros e códigos de saída
| O que você vê | Significado | Correção |
|---|---|---|
curl: (1) Protocol "mqtt" not supported | Seu build não tem MQTT | Use outro build ou a imagem curlimages/curl |
curl: (7) Failed to connect to ... port 1883 | Nada está escutando, ou um firewall bloqueia | Verifique host, porta e regras de firewall |
curl: (8) Weird server reply | O broker recusou o CONNECT | Rode com -v e leia o CONNACK |
Expected 0000 but got 0005 (com -v) | Código de retorno CONNACK 5: não autorizado | Adicione ou corrija -u user:pass, verifique as ACLs |
curl: (28) Operation timed out | O --max-time foi atingido | Esperado em assinaturas com limite de tempo |
Com -v, os dois últimos dígitos de Expected 0000 but got 00XX são o código de retorno CONNACK do MQTT 3.1.1: 01 versão de protocolo inaceitável, 02 identificador rejeitado, 03 servidor indisponível, 04 usuário ou senha inválidos, 05 não autorizado. A consulta de reason codes MQTT explica cada um, e reason codes do CONNACK detalha as causas.
Um smoke test com curl para scripts e CI
Os pontos fortes do curl são estar em todo lugar e retornar códigos de saída limpos. Este script assina em segundo plano, publica um token único e verifica se ele voltou:
#!/usr/bin/env bash
set -u
HOST="${MQTT_HOST:-broker.connectmq.com}"
TOPIC="testmqtt/curl-smoke/$(date +%s)-$$"
TOKEN="ping-$RANDOM"
OUT="$(mktemp)"
curl -sN --max-time 5 "mqtt://$HOST/$TOPIC" > "$OUT" &
SUB=$!
sleep 1 # dá tempo para o SUBSCRIBE chegar
if ! curl -s -d "$TOKEN" "mqtt://$HOST/$TOPIC"; then
echo "FALHA: publicação rejeitada (exit $?)"; exit 1
fi
wait "$SUB" # sai com 28 quando --max-time é atingido
if grep -q "$TOKEN" "$OUT"; then
echo "OK: mensagem roteada via $HOST"
else
echo "FALHA: mensagem não recebida"; exit 1
fi
rm -f "$OUT"Rode-o na máquina ou no pipeline que precisa acessar o broker: se passar, ele comprova DNS, o caminho TCP, o CONNECT e o roteamento de uma vez. Para uma versão mais completa com TLS e verificações de QoS, veja o script de smoke test MQTT e MQTT em scripts Bash.
Para ver as mensagens que seus comandos curl geram, abra o cliente MQTT online e assine o mesmo topic. Vê-las chegando no navegador confirma as duas direções de uma vez.
Veja as mensagens do curl no cliente online
O curl é o jeito mais rápido de responder "esta máquina alcança o broker e consegue publicar?" sem instalar nada. O suporte a MQTT dele é básico de propósito: QoS 0, sem retain, sem TLS e saída em formato binário. Use-o para smoke tests e verificações rápidas, e migre para mosquitto_pub, MQTT CLI ou uma biblioteca cliente quando precisar de segurança, confiabilidade ou uma saída legível.
Perguntas frequentes
O curl suporta MQTT sobre TLS (mqtts://)?
Não nas versões do curl que vêm na maioria dos sistemas: o manual do curl diz que MQTT sobre TLS não é suportado. Use o curl só contra a porta TCP 1883 sem criptografia para verificações rápidas, e use mosquitto_pub, MQTT CLI ou uma biblioteca para TLS na porta 8883.
O curl consegue publicar mensagens retidas ou com QoS 1?
Não. O curl publica com QoS 0 e com a flag retain desligada, e não existe opção para mudar isso. Se você precisa de mensagens retidas, QoS 1/2 ou propriedades do MQTT 5, use o mosquitto_pub ou o cliente online.
Por que o curl mostra caracteres estranhos antes de cada mensagem MQTT?
Ao assinar, o curl escreve cada PUBLISH recebido como 2 bytes com o tamanho do topic, o nome do topic e depois o payload, sem separador. Os dois primeiros bytes são binários e aparecem como lixo no terminal. Passe a saída pelo xxd ou por um pequeno parser para lê-la.


