Pourquoi cela nous importe : nous sommes Geonode et nous vendons des proxys ; les en-têtes de réponse constituent le moyen le plus rapide de répondre à la question que nos clients nous posent le plus souvent : s'agit-il d'un problème lié au proxy ou non ? Une page de blocage, une limitation de débit et une véritable erreur se présentent de manière identique dans un navigateur, mais sont complètement différentes au niveau des en-têtes. Une page «429 » avec l’en-tête « Retry-After » signifie que votre débit est trop élevé et qu’aucun proxy ne peut y remédier. Une page «403 » avec un en-tête « security-vendor » signifie que la cible vous a identifié. Une réponse de type « 407 » signifie que le proxy demande des identifiants. Lire les en-têtes avant de modifier quoi que ce soit évite bien des conjectures, et curl dispose d’un indicateur dédié à ce cas précis — %{proxy_used}, ajouté dans la version 8.7.0, qui renvoie 1 si le transfert est passé par un proxy. Utile lorsque vous n’êtes pas certain que votre configuration ait pris effet.
Les quatre options en un coup d'œil
| Option | Affiche | Envoie | Idéal pour |
|---|---|---|---|
-i | En-têtes de réponse + corps | Votre requête réelle | Vérification quotidienne |
-I | En-têtes de réponse uniquement | Une requête HEAD | Vérifications rapides, avec une mise en garde |
-D file | En-têtes de réponse vers un fichier | Votre requête réelle | Scripts, séparation des flux |
-v | En-têtes de requête et de réponse | Votre requête réelle | Débogage de ce que vous avez envoyé |
La ligne cruciale est la deuxième, et c’est elle qui est à l’origine de la plupart des confusions dans ce domaine. Tout le reste dépend de la destination de la sortie.
-i
: En-têtes avec le corps
L'option courante. Le manuel de curl la décrit comme suit : -i, --show-headers
: « Affiche les en-têtes de réponse dans la sortie… Cette option permet d’enregistrer les en-têtes de réponse dans le même flux/la même sortie que les données. »
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>...
Les en-têtes, une ligne vide, puis le corps — la même structure que le format « wire ».
Remarque concernant la nomenclature qui peut dérouter les lecteurs de documents plus anciens : la forme longue est désormais --show-headers
. Auparavant, c’était --include
, et les deux fonctionnent, mais la documentation actuelle utilise le nouveau nom.
Deux détails à connaître. Lorsque la sortie s’affiche sur un terminal, curl peut mettre les noms d’en-têtes en gras et marquer les URL de type Location:
, ce qui est utile en mode interactif mais indésirable dans un pipeline — --no-styled-output
permet de désactiver cette fonctionnalité. Et comme les en-têtes et le corps partagent un même flux, -i
avec -o file
écrit les deux dans le fichier, ce qui n’est presque jamais le résultat souhaité. Utilisez -D
dans ce cas.
-I : « Headers Only » (En-têtes uniquement) et pourquoi cela peut induire en erreur
-I est documenté comme suit : « Récupère uniquement les en-têtes. Les serveurs HTTP disposent de la commande HEAD, que cette méthode utilise pour ne récupérer que les en-têtes d’un document. »
Lisez attentivement ce qui précède. Elle ne récupère pas la réponse pour ensuite ignorer le corps. Elle envoie une méthode HTTP différente.
curl -I https://example.com
Il s’agit d’une requête « HEAD », et les conséquences sont bien réelles :
Certains serveurs gèrent la requête HEAD différemment. Une requête HEAD peut renvoyer des en-têtes différents, un code d’état différent, ou être rejetée purement et simplement avec une erreur « 405 Method Not Allowed » — alors que la requête équivalente GET fonctionne parfaitement.
Certains frameworks ne calculent pas le corps pour les requêtes HEAD, de sorte que les éléments Content-Length, ETag et Content-Type peuvent être absents ou erronés.
Les CDN et les caches traitent souvent les requêtes HEAD comme une clé de cache distincte, ce qui fait que les en-têtes de cache peuvent différer de ce qu’une requête réelle verrait.
Les systèmes anti-bots peuvent répondre différemment. Une requête « HEAD » provenant d’un client inhabituel est en soi un signal, et le défi que vous recevez peut ne pas être celui qu’une requête « GET » produirait.
Ainsi, -I est excellent pour des vérifications rapides — cette URL est-elle active, vers quoi redirige-t-elle, quelle est la taille du fichier — mais peu fiable pour déboguer les raisons pour lesquelles une requête « GET » se comporte de manière étrange. Lorsque vous analysez une requête réelle, utilisez la méthode appropriée :
curl -sS -o /dev/null -D - https://example.com
Cela effectue une requête GET normale, redirige le corps vers /dev/null et affiche les en-têtes sur la sortie standard. Cela vous donne ce que -I semble vous fournir, sans modifier la requête.
Si vous avez spécifiquement besoin des en-têtes d’une requête POST, le même principe s’applique :
curl -sS -o /dev/null -D - -X POST -H "Content-Type: application/json" \
-d '{"a":1}' https://api.example.com/items
-D et -v : Séparation des flux et visualisation des requêtes
** La commande -D écrit les en-têtes vers une destination distincte.** Extrait du manuel : « Écrit les en-têtes de protocole reçus dans le fichier spécifié… Spécifiez « - » comme nom de fichier (un seul signe moins) pour les écrire sur la sortie standard (stdout). » Il précise également que si aucun en-tête n’est reçu, l’option « crée un fichier vide » — ce qui constitue en soi une information de diagnostic.
curl -D headers.txt -o body.html https://example.com
Une séparation nette, ce qui est exactement ce que l’on recherche dans les scripts. -D - envoie les en-têtes vers stdout tandis que le corps est redirigé vers l’emplacement indiqué par -o, et cette combinaison constitue la base du modèle ci-dessus.
-v affiche également la requête, ce qui correspond souvent à la partie dont vous avez réellement besoin. Le manuel explique précisément les préfixes :
Les lignes de sortie détaillées sont préfixées par des lettres :
>en-tête envoyé par curl,<en-tête reçu par curl,}données envoyées par curl,{données reçues par curl,*informations supplémentaires fournies par curl.
curl -v https://example.com 2>&1 | grep '^>'
Cela vous donne exactement ce que curl a transmis — ce qui n’est souvent pas ce que vous avez configuré, car les bibliothèques, les valeurs par défaut et les fichiers d’.curlrcs ajoutent et remplacent tous des en-têtes. Un grand nombre de problèmes du type « le serveur ignore mon en-tête » trouvent ici leur solution.
Notez que la sortie détaillée est dirigée vers stderr, c’est pourquoi la commande 2>&1 est nécessaire avant le redirection. C’est un choix délibéré : cela permet de garder le corps du flux propre sur stdout.
Le manuel signale également que depuis curl 8.10, la répétition de l’-v augmente le niveau de trace. Pour les opérations véritablement de bas niveau, --trace-ascii fournit « un vidage complet de toutes les données entrantes et sortantes, y compris des informations descriptives », en omettant les valeurs hexadécimales afin que le résultat reste lisible.
Le manuel formule un avertissement qui mérite d’être répété : les sorties « trace » et « verbose » « peuvent contenir des données sensibles, notamment des noms d’utilisateur, des identifiants ou des données confidentielles. Soyez vigilant et prudent lorsque vous partagez des journaux de trace avec d’autres personnes ». Les identifiants de proxy transmis dans une URL apparaissent dans la sortie « verbose ». Masquez-les avant de les coller dans un outil de suivi des incidents.
En-têtes lisibles par machine avec ``%{header_json}`
` : cette option, que la plupart des gens n’ont jamais vue, a été ajoutée dans curl 7.83.0 et constitue la solution idéale chaque fois que vous vous apprêtez à écrire une expression régulière sur le texte d’un en-tête.
Le manuel la décrit comme « un objet JSON contenant tous les en-têtes de réponse HTTP du transfert récent. Les valeurs sont fournies sous forme de tableaux, car en cas d’en-têtes multiples, il peut y avoir plusieurs valeurs. » Les noms d’en-têtes sont fournis « en minuscules, classés par ordre d’apparition sur le réseau », les doublons étant « regroupés à la première occurrence de cet en-tête, chaque valeur étant présentée dans le tableau 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=/"]
}
Cela résout trois problèmes à la fois. Les noms sont normalisés en minuscules, ce qui évite toute correspondance insensible à la casse. Les en-têtes répétés, tels que Set-Cookie
, sont transmis sous forme de tableaux plutôt que d’être tacitement regroupés. Et le résultat est analysable sans avoir à écrire de parseur.
Extraire un seul en-tête devient alors très simple :
curl -s -o /dev/null -w '%{header_json}' "$URL" | jq -r '.["retry-after"][0] // "none"'
Autres variables d’-w
s qui s’associent bien avec celle-ci :
curl -s -o /dev/null -w 'status=%{response_code} redirects=%{num_redirects} proxy=%{proxy_used} ip=%{remote_ip}\n' "$URL"
response_code
correspond au statut du dernier transfert, num_redirects
compte les redirections suivies, redirect_url
indique où une redirection aurait mené si vous n’aviez pas utilisé -L
, remote_ip
est l’adresse à laquelle la connexion a effectivement été établie, et proxy_used
renvoie 1 si un proxy a été impliqué. Cette dernière variable est vraiment utile lorsqu’un modèle de type NO_PROXY
a pu exclure discrètement votre hôte.
Suivi des chaînes de redirection
Sans l'option ``-L`
, curl s'arrête à la première redirection et vous ne voyez que cette réponse. Avec l'option ``-L
`, curl affiche les en-têtes de chaque réponse de la chaîne :
curl -sSL -o /dev/null -D - https://example.com
HTTP/2 301
location: https://www.example.com/
HTTP/2 200
content-type: text/html
Chaque bloc correspond à un saut. C'est ainsi que vous pouvez découvrir qu'une URL effectue trois redirections, qu'un saut bascule en HTTP standard ou qu'une redirection perd un cookie.
Deux formats à retenir :
curl -sSL -o /dev/null -w '%{num_redirects} hops -> %{url_effective}\n' "$URL"
Le nombre de sauts et la destination finale sur une seule ligne. Et lorsque vous souhaitez voir où mène une redirection sans la suivre :
curl -s -o /dev/null -w '%{redirect_url}\n' "$URL"
Les chaînes de redirection méritent d’être inspectées plus souvent qu’on ne le fait habituellement. Chaque saut correspond à un aller-retour ; une chaîne de quatre sauts ajoute une latence réelle, et un saut inattendu via un hôte différent est souvent à l’origine d’un problème de cookie ou de CORS.
En-têtes via un proxy
Deux éléments viennent compléter le tableau, qui peuvent tous deux prêter à confusion la première fois.
** Les réponses «CONNECT
» apparaissent dans la sortie détaillée.** Pour le protocole HTTPS via un proxy HTTP, curl envoie d’abord une requête « CONNECT
» afin d’établir un tunnel, et cet échange comporte ses propres en-têtes :
curl -v -x http://proxy.example.com:8080 https://example.com
Vous verrez d’abord « CONNECT
», puis « HTTP/1.1 200 Connection established
» provenant du proxy, et ce n’est qu’ensuite que la requête réelle apparaîtra. Ce premier bloc correspond à la communication du proxy, et non à celle de la cible. Les confondre est une erreur courante chez les débutants. L’option ``--suppress-connect-headers les supprime de la sortie lorsque vous ne vous intéressez qu’à la réponse de la cible.
%{http_connect}
indique le code de réponse du proxy à la requête CONNECT en particulier, indépendamment du statut de la cible. Cette distinction est précisément ce dont vous avez besoin lorsqu’une erreur survient et que vous ne savez pas quel maillon a refusé l’accès :
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
signifie que le proxy a fonctionné et que la cible vous a refusé l’accès. connect=407
signifie que le proxy a demandé des identifiants et n’a jamais atteint la cible. Ces deux situations nécessitent des solutions complètement différentes, et sans cette distinction, elles semblent identiques du point de vue de l’application.
Notez également que pour le protocole HTTPS transitant par un tunnel, le proxy ne peut ni ajouter ni lire d’en-têtes : il se contente de relayer des octets chiffrés. Si vous constatez la présence d’en-têtes inattendus dans une réponse HTTPS, ceux-ci proviennent de la cible ou d’un CDN situé en amont de celle-ci, et non du proxy.
Ce que les en-têtes vous révèlent réellement
Le but de tout cela. Les lire attentivement permet de passer de la conjecture au diagnostic.
Commençons par la ligne d’état. 200 : opération réussie. 301/302 : redirection. 403 : refusé. 429 : limitation de débit. 407 : authentification par proxy. 502/503 : problème en amont.
Retry-After s’affiche avec 429 et 503 et vous indique exactement combien de temps vous devez attendre. Respecter ce délai est à la fois la bonne chose à faire et le moyen le plus rapide de rétablir le fonctionnement. L’ignorer et réessayer immédiatement, c’est ainsi qu’une limite temporaire se transforme en une limite plus longue.
Content-Type vous indique ce que vous avez réellement reçu. La présence de « text/html » sur un point de terminaison d’API signifie que vous avez obtenu une page d’erreur, et non du JSON ; c’est la réponse à une grande partie des échecs d’analyse.
Content-Length par rapport à ce qui est arrivé. Un corps court avec une longueur déclarée importante indique une troncature.
Les en-têtes de cache — Cache-Control, ETag, Last-Modified — vous indiquent si vous pouvez éviter une nouvelle récupération. If-None-Match et If-Modified-Since lors des requêtes suivantes transforment un transfert complet en 304, ce qui, sur une bande passante facturée au volume, représente une économie directe.
Set-Cookie indique l’état de session que le serveur établit, et son absence là où vous vous attendiez à en trouver un explique bon nombre de problèmes d’authentification.
Server et les en-têtes spécifiques aux fournisseurs identifient ce qui se trouve en amont de l’origine. Une réponse comportant les en-têtes d’un fournisseur de sécurité avec un 403 indique que le blocage provient d’une couche de protection plutôt que de l’application — un problème différent nécessitant une réponse différente.
En-têtes non standard. Les budgets de limitation de débit, les identifiants de requête et les métadonnées spécifiques à l’API apparaissent souvent sous la forme d’en-têtes préfixés par x-, et constituent fréquemment les éléments les plus utiles de la réponse. C’est l’identifiant de requête que le service d’assistance vous demandera.
Questions fréquentes
Comment afficher les en-têtes de réponse avec curl ?
curl -i URL affiche les en-têtes suivis du corps de la réponse. curl -D - URL affiche les en-têtes séparément sur la sortie standard (stdout). curl -v URL affiche à la fois les en-têtes de la requête et ceux de la réponse. Évitez d'utiliser -I lors du débogage d'une requête réelle, car cette commande envoie une requête HEAD plutôt qu'une requête GET.
Quelle est la différence entre -i et -I dans curl ?
-i inclut les en-têtes de réponse en plus du corps de votre requête réelle. -I envoie quant à lui une requête HEAD ; il s’agit donc d’une requête différente pouvant donner des résultats différents. Utilisez -i ou -o /dev/null -D - lorsque vous avez besoin des en-têtes de la requête que vous déboguez réellement.
Comment afficher uniquement les en-têtes sans le corps de la requête ?
curl -sS -o /dev/null -D - URL. Cette commande effectue une requête GET normale, ignore le corps de la requête et affiche les en-têtes. Elle vous donne le même résultat que -I sans modifier la méthode HTTP, ce qui est important car certains serveurs répondent différemment à une requête HEAD ou la rejettent purement et simplement.
Comment afficher les en-têtes de requête envoyés par curl ?
curl -v URL et recherchez les lignes commençant par >, qui correspondent aux en-têtes envoyés par curl. Les informations détaillées sont redirigées vers stderr ; utilisez donc un tuyau vers 2>&1 si vous souhaitez les filtrer. C’est ainsi que vous pouvez vérifier que l’en-tête que vous avez configuré est bien transmis.
Comment obtenir les en-têtes de curl au format JSON ?
curl -s -o /dev/null -w '%{header_json}' URL. Ajoutée dans la version 7.83.0 de curl, cette option renvoie tous les en-têtes de réponse sous forme d’objet JSON avec des noms en minuscules et des valeurs sous forme de tableaux ; ainsi, les en-têtes répétés tels que Set-Cookie sont conservés plutôt que regroupés. Redirigez le flux vers jq pour extraire les champs.
Pourquoi vois-je deux ensembles d’en-têtes lorsque j’utilise un proxy ?
Pour les connexions HTTPS via un proxy HTTP, curl envoie d’abord une requête CONNECT afin d’ouvrir un tunnel, et la réponse du proxy à cette requête apparaît avant celle de la cible. Utilisez --suppress-connect-headers pour la masquer, ou %{http_connect} pour lire le code d’état du proxy séparément de celui de la cible.
Comment puis-je voir les en-têtes de chaque redirection ?
Ajoutez -L pour que curl suive les redirections, puis utilisez -D - ou -i — curl affiche les en-têtes de chaque réponse de la chaîne, un bloc par saut. %{num_redirects} et %{url_effective} vous donnent le nombre de sauts et l’URL finale sur une seule ligne.
Les en-têtes de réponse indiquent-elles si je suis bloqué ?
Souvent, oui, et de manière plus fiable que le corps de la requête. Une réponse de type « 429 » avec « Retry-After » indique une limitation de débit. Une réponse de type « 403 » comportant les en-têtes d’un fournisseur de sécurité correspond à une couche de protection. Une réponse de type « 200 » avec « Content-Type: text/html » sur un point de terminaison d’API correspond à une page d’authentification ou de connexion. Chacune de ces situations implique une solution différente, et seuls les en-têtes permettent de les distinguer.
En conclusion
Quatre options et un piège courant. -i pour les vérifications quotidiennes, -D - lorsque vous souhaitez séparer les en-têtes du corps du message, -v lorsque vous avez besoin de voir à la fois ce que vous avez envoyé et ce qui vous a été renvoyé, et -I uniquement pour des vérifications rapides de disponibilité — car cette option envoie une requête HEAD, et les serveurs sont en droit de répondre différemment à une requête HEAD par rapport à une requête GET.
Si vous ne devez retenir qu’une seule chose de cet article, optez pour %{header_json}. Tout script analysant actuellement le texte des en-têtes à l’aide d’une expression régulière devrait l’utiliser à la place : noms en minuscules, tableaux pour les en-têtes répétées et sortie lisible par jq. Associé à %{response_code}, %{num_redirects} et %{proxy_used}, cet outil transforme l’inspection des en-têtes en une opération que vous pouvez vérifier de manière formelle plutôt que de vous fier à votre seul œil.
Et lorsqu’une requête échoue, consultez les en-têtes avant de modifier quoi que ce soit. Le code d’état, Retry-After, Content-Type et les éventuels en-têtes spécifiques au fournisseur qui les accompagnent indiquent généralement clairement la nature du problème — ce qui vaut bien de modifier les paramètres au hasard jusqu’à ce que quelque chose fonctionne, et ne prend qu’une dizaine de secondes.
