A razão pela qual nos preocupamos: somos a Geonode e vendemos proxies, e os cabeçalhos de resposta são a forma mais rápida de responder à pergunta que os clientes mais nos fazem — isto é um problema de proxy ou não? Uma página de bloqueio, um limite de taxa e um erro genuíno parecem idênticos num navegador, mas são completamente diferentes nos cabeçalhos. Um 429 com Retry-After significa que está a ir demasiado depressa e nenhum proxy resolve isso. Um 403 com um cabeçalho «security-vendor» significa que o destino o identificou. Um 407 significa que o proxy pede credenciais. Ler os cabeçalhos antes de alterar qualquer coisa evita muitas suposições, e o curl tem um sinalizador para este caso específico — %{proxy_used}, adicionado na versão 8.7.0, que devolve 1 se a transferência tiver passado por um proxy. É útil quando não tem a certeza de que a sua configuração entrou em vigor.
As quatro opções num relance
| Opção | Mostra | Envia | Ideal para |
|---|---|---|---|
-i | Cabeçalhos de resposta + corpo | A sua solicitação real | Inspeção diária |
-I | Apenas cabeçalhos de resposta | Uma solicitação HEAD | Verificações rápidas, com uma ressalva |
-D file | Cabeçalhos de resposta para um ficheiro | A sua solicitação real | Criação de scripts, separação de fluxos |
-v | Cabeçalhos de pedido e resposta | O seu pedido real | Depuração do que enviou |
A linha crítica é a segunda, e é a fonte da maior parte da confusão nesta área. Tudo o resto é uma questão de para onde vai a saída.
-i
: Cabeçalhos com o corpo
A opção mais comum. O manual do curl descreve-a como -i, --show-headers
: «Mostrar os cabeçalhos da resposta na saída... Esta opção faz com que os cabeçalhos da resposta sejam guardados no mesmo fluxo/saída que os dados.»
curl -i https://example.com
HTTP/2 200
content-type: text/html; charset=UTF-8
content-length: 1256
cache-control: max-age=604800
date: Wed, 02 Sep 2026 10:14:22 GMT
<!doctype html>...
Cabeçalhos, uma linha em branco e, em seguida, o corpo — a mesma estrutura do formato de transmissão.
Uma nota sobre a nomenclatura que pode confundir quem estiver a ler material mais antigo: a forma completa é agora --show-headers
. Anteriormente era --include
, e ambas funcionam, mas a documentação atual utiliza o nome mais recente.
Dois detalhes que vale a pena conhecer. Quando a saída é direcionada para um terminal, o curl pode apresentar os nomes dos cabeçalhos em negrito e assinalar URLs do tipo Location:
, o que é útil em modo interativo, mas indesejável num pipeline — --no-styled-output
desativa essa funcionalidade. E como os cabeçalhos e o corpo partilham um fluxo, -i
com -o file
grava ambos no ficheiro, o que quase nunca é o que se pretende. Utilize -D
nesse caso.
-I: Apenas cabeçalhos e por que razão isso pode induzir em erro
-I está documentado da seguinte forma: «Recolhe apenas os cabeçalhos. Os servidores HTTP dispõem do comando HEAD, que este utiliza para obter apenas o cabeçalho de um documento.»
Leia isto com atenção. Não recolhe a resposta e descarta o corpo. Envia um método HTTP diferente.
curl -I https://example.com
Trata-se de um pedido HEAD, e as consequências são reais:
Alguns servidores tratam o HEAD de forma diferente. Um HEAD pode devolver cabeçalhos diferentes, um código de estado diferente ou ser totalmente rejeitado com um 405 Method Not Allowed — enquanto o equivalente GET funciona na perfeição.
Algumas estruturas (frameworks) não calculam o corpo para HEAD, pelo que Content-Length, ETag e Content-Type podem estar ausentes ou incorretos.
As CDNs e as caches tratam frequentemente o HEAD como uma chave de cache distinta, pelo que os cabeçalhos de cache podem diferir do que uma solicitação real veria.
Os sistemas anti-bot podem responder de forma diferente. Um HEAD proveniente de um cliente invulgar é, por si só, um sinal, e o desafio que receber pode não ser o mesmo que um GET produziria.
Por isso, -I é excelente para verificações rápidas — se esta URL está ativa, para onde redireciona, qual é o tamanho do ficheiro — mas não é fiável para depurar a razão pela qual um GET se comporta de forma estranha. Quando estiver a diagnosticar um pedido real, utilize o método adequado:
curl -sS -o /dev/null -D - https://example.com
Isto executa um pedido normal GET, descarta o corpo para /dev/null e apresenta os cabeçalhos na saída padrão. Proporciona-lhe o que -I parece oferecer, sem alterar o pedido.
Se precisar especificamente dos cabeçalhos de um POST, aplica-se o mesmo padrão:
curl -sS -o /dev/null -D - -X POST -H "Content-Type: application/json" \
-d '{"a":1}' https://api.example.com/items
-D e -v: Separar fluxos e visualizar pedidos
** O comando-D grava os cabeçalhos num destino separado.** O manual: «Grave os cabeçalhos de protocolo recebidos no ficheiro especificado... Especifique «-» como nome do ficheiro (um único sinal de menos) para que sejam gravados na saída padrão (stdout).» Também refere que, se não forem recebidos cabeçalhos, a opção «cria um ficheiro vazio» — o que, por si só, constitui informação de diagnóstico.
curl -D headers.txt -o body.html https://example.com
Separação clara, que é o que se pretende em scripts. -D - envia os cabeçalhos para a saída padrão (stdout), enquanto o corpo é direcionado para onde -o indicar, e essa combinação é a base do padrão acima.
-v também mostra o pedido, que é frequentemente a parte de que realmente precisa. O manual explica os prefixos com precisão:
As linhas de saída detalhadas são precedidas por letras:
>cabeçalho enviado pelo curl,<cabeçalho recebido pelo curl,}dados enviados pelo curl,{dados recebidos pelo curl,*informação adicional fornecida pelo curl.
curl -v https://example.com 2>&1 | grep '^>'
Isto mostra-lhe exatamente o que o curl transmitiu — o que, muitas vezes, não corresponde ao que configurou, porque as bibliotecas, os valores predefinidos e os ficheiros .curlrc adicionam e substituem cabeçalhos. Muitos dos problemas do tipo «o servidor está a ignorar o meu cabeçalho» resolvem-se aqui.
Note que a saída detalhada vai para o stderr, razão pela qual é necessário o comando 2>&1 antes do redirecionamento. Isto é deliberado: mantém o corpo da saída limpo no stdout.
O manual também indica que, desde o curl 8.10, repetir «-v» aumenta o nível de rastreio. Para trabalhos verdadeiramente de baixo nível, «--trace-ascii» fornece «um registo completo de todos os dados de entrada e saída, incluindo informação descritiva», com o código hexadecimal omitido para que se mantenha legível.
Há um aviso no manual que vale a pena repetir: a saída de rastreio e a saída detalhada «podem conter dados sensíveis, incluindo nomes de utilizador, credenciais ou conteúdo de dados confidenciais. Esteja atento e tenha cuidado ao partilhar registos de rastreio com outras pessoas.» As credenciais de proxy passadas numa URL aparecem na saída detalhada. Ocultem-nas antes de as colarem num sistema de acompanhamento de problemas.
Cabeçalhos legíveis por máquina com ``%{header_json}`
`: a opção que a maioria das pessoas nunca viu, adicionada no curl 7.83.0, e a resposta correta sempre que estivesse prestes a escrever uma expressão regular para analisar o texto de um cabeçalho.
O manual descreve-a como «Um objeto JSON com todos os cabeçalhos de resposta HTTP da transferência recente. Os valores são fornecidos como matrizes, uma vez que, no caso de múltiplos cabeçalhos, pode haver múltiplos valores.» Os nomes dos cabeçalhos são apresentados «em minúsculas, listados pela ordem em que aparecem na rede», com as duplicatas «agrupadas na primeira ocorrência desse cabeçalho, sendo cada valor apresentado na matriz JSON».
curl -s -o /dev/null -w '%{header_json}' https://example.com | jq
{
"content-type": ["text/html; charset=UTF-8"],
"cache-control": ["max-age=604800"],
"set-cookie": ["a=1; Path=/", "b=2; Path=/"]
}
Isto resolve três coisas de uma só vez. Os nomes são normalizados para minúsculas, pelo que não há correspondência insensível a maiúsculas e minúsculas. Cabeçalhos repetidos, como Set-Cookie
, são apresentados como matrizes em vez de serem silenciosamente agrupados. E a saída é analisável sem necessidade de escrever um analisador.
Extrair um único cabeçalho torna-se trivial:
curl -s -o /dev/null -w '%{header_json}' "$URL" | jq -r '.["retry-after"][0] // "none"'
Outras variáveis -w
que combinam bem com esta:
curl -s -o /dev/null -w 'status=%{response_code} redirects=%{num_redirects} proxy=%{proxy_used} ip=%{remote_ip}\n' "$URL"
response_code
é o estado da última transferência, num_redirects
conta os redirecionamentos seguidos, redirect_url
mostra para onde um redirecionamento teria levado caso não tivesse utilizado -L
, remote_ip
é o endereço ao qual se ligou efetivamente e proxy_used
devolve 1 se tiver estado envolvido um proxy. Esta última é verdadeiramente útil quando um padrão NO_PROXY
pode ter excluído silenciosamente o seu anfitrião.
Seguir cadeias de redirecionamento
Sem a opção «-L
», o curl pára no primeiro redirecionamento e só se vê essa resposta. Com -L
, o curl mostra os cabeçalhos de todas as respostas da cadeia:
curl -sSL -o /dev/null -D - https://example.com
HTTP/2 301
location: https://www.example.com/
HTTP/2 200
content-type: text/html
Cada bloco corresponde a um salto. É assim que se descobre que um URL redireciona três vezes, que um salto passa para HTTP simples ou que um redirecionamento perde um cookie.
Dois padrões que vale a pena manter:
curl -sSL -o /dev/null -w '%{num_redirects} hops -> %{url_effective}\n' "$URL"
A contagem e o destino final numa única linha. E quando quiser ver para onde aponta um redirecionamento sem o seguir:
curl -s -o /dev/null -w '%{redirect_url}\n' "$URL"
Vale a pena inspecionar as cadeias de redirecionamento com mais frequência do que as pessoas costumam fazer. Cada salto é uma viagem de ida e volta; uma cadeia de quatro salvos acrescenta latência real; e um salto inesperado através de um host diferente é frequentemente a explicação para um problema de cookie ou CORS.
Cabeçalhos através de um proxy
Duas novidades a acrescentar ao quadro, ambas as quais causam confusão à primeira vista.
** As respostas «CONNECT
» aparecem na saída detalhada.** No caso de HTTPS através de um proxy HTTP, o curl envia primeiro um pedido «CONNECT
» para estabelecer um túnel, e essa troca de dados tem os seus próprios cabeçalhos:
curl -v -x http://proxy.example.com:8080 https://example.com
Irá ver o CONNECT
, um HTTP/1.1 200 Connection established
do proxy e, só depois, o pedido real. Esse primeiro bloco é o proxy a comunicar, não o destino. Confundi-los é um erro comum no início. --suppress-connect-headers
remove-os da saída quando só está interessado na resposta do destino.
%{http_connect}
apresenta o código de resposta do proxy especificamente para o CONNECT, separadamente do estado do destino. Essa distinção é precisamente o que precisa quando algo falha e não sabe qual dos saltos recusou o pedido:
curl -s -o /dev/null -x "$PROXY" \
-w 'connect=%{http_connect} status=%{response_code} proxy=%{proxy_used}\n' \
https://example.com
connect=200 status=403
significa que o proxy funcionou e que o destino recusou o pedido. connect=407
significa que o proxy solicitou credenciais e nunca chegou ao destino. Essas duas situações têm soluções completamente diferentes e, sem esta distinção, parecem idênticas do ponto de vista da aplicação.
Note também que, no caso de HTTPS através de um túnel, o proxy não pode adicionar nem ler cabeçalhos — está apenas a retransmitir bytes encriptados. Se observar cabeçalhos inesperados numa resposta HTTPS, estes provêm do destino ou de uma CDN à sua frente, e não do proxy.
O que os cabeçalhos realmente revelam
O objetivo de tudo isto. Interpretá-los corretamente transforma as suposições em diagnósticos.
Primeiro, a linha de estado. 200 foi bem-sucedido. 301/302 redirecionado. 403 recusado. 429 com limitação de taxa. 407 autenticação de proxy. 502/503 problema no servidor de origem.
Retry-After aparece juntamente com 429 e 503 e indica-lhe exatamente quanto tempo deve esperar. Respeitar esse tempo é a atitude correta e a forma mais rápida de voltar a ter o serviço a funcionar. Ignorá-lo e tentar novamente de imediato é o que faz com que um limite temporário se torne mais prolongado.
Content-Type indica o que recebeu efetivamente. text/html num ponto final da API significa que recebeu uma página de erro, e não JSON, e é a resposta para uma grande parte das falhas de análise.
Content-Length versus o que chegou. Um corpo curto com um comprimento declarado grande significa truncamento.
Cabeçalhos de cache — Cache-Control, ETag, Last-Modified — indicam se é possível evitar uma nova recuperação. If-None-Match e If-Modified-Since em pedidos subsequentes transformam uma transferência completa numa 304, o que, em largura de banda medida, representa uma poupança direta.
Set-Cookie mostra qual o estado de sessão que o servidor está a estabelecer, e a sua ausência onde se esperava que existisse explica muitos enigmas de autenticação.
Server e cabeçalhos específicos do fornecedor identificam o que se encontra à frente da origem. Uma resposta que contenha cabeçalhos de um fornecedor de segurança com 403 indica que o bloqueio provém de uma camada de proteção e não da aplicação — um problema diferente que requer uma resposta diferente.
Cabeçalhos não padronizados. Limites de taxa, identificadores de pedido e metadados específicos da API aparecem frequentemente como cabeçalhos com o prefixo x-, e são muitas vezes a informação mais útil na resposta. Um ID de pedido é o que o suporte técnico irá solicitar.
Perguntas frequentes
Como posso ver os cabeçalhos da resposta com o curl?
curl -i URL mostra os cabeçalhos seguidos do corpo da resposta. curl -D - URL escreve os cabeçalhos na saída padrão (stdout) separadamente. curl -v URL mostra tanto os cabeçalhos da solicitação como os da resposta. Evite -I ao depurar uma solicitação real, pois envia um HEAD em vez de um GET.
Qual é a diferença entre -i e -I no curl?
-i inclui os cabeçalhos de resposta juntamente com o corpo da sua solicitação real. -I envia, em vez disso, uma solicitação HEAD, pelo que se trata de uma solicitação diferente com resultados potencialmente diferentes. Utilize -i ou -o /dev/null -D - quando precisar dos cabeçalhos da solicitação que está efetivamente a depurar.
Como posso ver apenas os cabeçalhos sem o corpo?
curl -sS -o /dev/null -D - URL. Isto executa um GET normal, descarta o corpo e apresenta os cabeçalhos. Proporciona-lhe o que -I parece oferecer sem alterar o método HTTP, o que é importante porque alguns servidores respondem ao HEAD de forma diferente ou rejeitam-no diretamente.
Como posso ver os cabeçalhos da solicitação que o curl envia?
curl -v URL e procure as linhas que começam por >, que são os cabeçalhos enviados pelo curl. A saída detalhada é direcionada para o stderr, por isso utilize o comando «pipe» com 2>&1 se quiser filtrá-la. É assim que se confirma que o cabeçalho que configurou está efetivamente a ser transmitido.
Como obtenho os cabeçalhos do curl em formato JSON?
curl -s -o /dev/null -w '%{header_json}' URL. Adicionado no curl 7.83.0, este comando emite todos os cabeçalhos de resposta como um objeto JSON com nomes em minúsculas e valores em forma de matriz, pelo que cabeçalhos repetidos, como Set-Cookie, são preservados em vez de serem agrupados. Envie os dados para jq para extrair os campos.
Por que vejo dois conjuntos de cabeçalhos quando utilizo um proxy?
Para HTTPS através de um proxy HTTP, o curl envia primeiro um CONNECT para abrir um túnel, e a resposta do proxy a esse pedido aparece antes da do destino. Utilize --suppress-connect-headers para a ocultar, ou %{http_connect} para ler o código de estado do proxy separadamente do do destino.
Como posso ver os cabeçalhos de cada redirecionamento?
Adicione -L para que o curl siga os redirecionamentos e utilize -D - ou -i — o curl apresenta os cabeçalhos de todas as respostas na cadeia, um bloco por salto. %{num_redirects} e %{url_effective} fornecem a contagem e o URL final numa única linha.
Os cabeçalhos de resposta revelam se estou a ser bloqueado?
Na maioria das vezes, sim, e de forma mais fiável do que o corpo da resposta. Um 429 com Retry-After indica um limite de taxa. Um 403 com cabeçalhos de um fornecedor de segurança indica uma camada de proteção. Um 200 com Content-Type: text/html num ponto final de API indica uma página de desafio ou de início de sessão. Cada uma destas situações implica uma solução diferente, e só os cabeçalhos permitem distingui-las.
Conclusão
Quatro opções e uma armadilha comum. -i para inspeção diária, -D - quando quiser que os cabeçalhos fiquem separados do corpo, -v quando precisar de ver o que enviou, bem como o que recebeu, e -I apenas para verificações rápidas de disponibilidade — porque envia um HEAD, e os servidores têm o direito de responder a um HEAD de forma diferente de um GET.
A opção que vale a pena adotar, se tiver de ficar com apenas uma coisa disto, é %{header_json}. Qualquer script que atualmente analise o texto dos cabeçalhos com uma expressão regular deve utilizá-la em vez disso: nomes em minúsculas, matrizes para cabeçalhos repetidos e saída que jq consiga ler. Em conjunto com %{response_code}, %{num_redirects} e %{proxy_used}, transforma a inspeção de cabeçalhos em algo que pode ser verificado de forma sistemática, em vez de apenas a olho nu.
E quando uma solicitação corre mal, leia os cabeçalhos antes de alterar qualquer coisa. O código de estado, Retry-After, Content-Type e quaisquer cabeçalhos de fornecedor entre eles geralmente identificam o problema de forma direta — o que é melhor do que ir alterando as configurações até que algo funcione, e demora cerca de dez segundos.
