Inspecter une requête HTTP : curl, message brut et HAR

Inspecter une requête HTTP : curl, message brut et HAR

Formatez un Copy as cURL, analysez une requête ou une réponse HTTP brute et lisez un export HAR dans votre navigateur : rien n’est envoyé ni rejoué.

07.10.2026
18 min de lecture
Partager cet article:
HTTPcURLHARDébogageTutoriel

Une requête, trois façons de la lire : commande cURL, message brut, fichier HAR

Quand une intégration d’API se comporte mal, la preuve est presque toujours un échange HTTP que l’on peut copier depuis les DevTools. Le problème, c’est la forme sous laquelle il arrive : le Copy as cURL de Chrome tient en une seule ligne interminable, une réponse collée est un bloc d’en-têtes suivi d’un corps, et un export HAR est un fichier JSON de plusieurs centaines d’entrées. Le formateur cURL transforme un Copy as cURL ou un Copy as fetch en cURL multiligne lisible, ou en HTTP/1.1, fichier .http, fetch JavaScript, Python requests, Go ou PHP. Le parseur de message HTTP lit une requête brute, une réponse ou un bloc d’en-têtes, garde les en-têtes en double et met en forme un corps JSON. L’inspecteur HAR ouvre toute une session enregistrée, la réduit à l’appel qui échoue et recopie cet appel en cURL. Les trois outils travaillent sur du texte collé, dans votre navigateur : FastMinify n’envoie jamais la requête, ne va jamais chercher l’URL collée et n’a ni exécuteur ni proxy, donc un jeton présent dans le collage reste dans votre onglet. Ce guide explique quel outil répond à quelle question, comment déboguer une requête qui marche dans le navigateur mais pas dans votre script, les erreurs qui font fuiter un identifiant ou qui induisent en erreur, l’anatomie d’un message HTTP, puis les mêmes vérifications dans un terminal et en Node. Les outils se trouvent dans le hub des outils HTTP.

Formateur cURL : lit le Copy as cURL et le Copy as fetch de Chrome (analysés comme du texte, jamais exécutés), un fichier .http à requête unique ou une requête HTTP/1.1 brute. Sortie en cURL multiligne ou sur une ligne, HTTP/1.1, .http, fetch JavaScript, Python requests, Go net/http ou PHP curl
L’option Retirer les secrets (désactivée par défaut dans le formateur) retire de la copie les en-têtes Cookie, Authorization, Referer et sec-ch-*, ainsi que les valeurs de -u et -b
Parseur de message HTTP : distingue une requête, une réponse et un bloc d’en-têtes seuls, accepte les fins de ligne LF ou CRLF, garde les en-têtes en double et peut mettre en forme un corps JSON. Il nettoie aussi la sortie collée de <code>curl -v</code> (les lignes qui commencent par > et <)
Inspecteur HAR : ouvre un fichier .har ou .json jusqu’à 50 Mo (avertissement au-delà de 20 Mo), filtre les entrées par type, méthode, statut et URL, et copie un appel en cURL ou en HTTP/1.1. Son option Sanitizer les secrets est activée par défaut
Aucun bouton Envoyer : la requête n’est jamais exécutée, donc pas d’erreur CORS à combattre ni d’effet de bord sur votre API
Tout se passe dans l’onglet : aucune commande, aucun en-tête, aucun HAR collé n’est téléversé

Quel outil pour quelle question

Les bons usages

Ces trois outils servent à lire, traduire et partager un échange HTTP. Ils ne l’exécutent pas.

Rendre lisible un Copy as cURL des DevTools avant de le mettre dans un ticket, un README ou une revue de code : un en-tête par ligne au lieu d’un bloc compact
Traduire la même requête en fetch, Python requests, Go ou PHP pour reproduire un appel dans le langage de votre projet
Vérifier une réponse collée par un collègue : ligne de statut, en-têtes en double (plusieurs lignes Set-Cookie, Vary) et corps JSON, dans le <a href="/fr/http-message-parser" class="text-primary hover:underline">parseur de message HTTP</a>
Coller la sortie de <code>curl -v</code> et récupérer la requête ou la réponse sans les lignes de journal <code>*</code>
Retrouver l’appel en échec parmi des centaines dans un HAR grâce à la pastille Erreurs, puis le copier en cURL pour le rejouer depuis un terminal
Ce qui relève d’autre chose

Chaque outil s’arrête là où un autre commence. Passez la main aux outils voisins ou à votre propre code.

Rejouer une requête : il n’y a pas d’exécuteur. Collez le cURL dans un terminal, dans Postman ou dans votre suite de tests
Décomposer une URL en protocole, hôte, chemin et lignes de requête, ou retirer les paramètres de suivi : l’<a href="/fr/url-parser" class="text-primary hover:underline">analyseur d’URL</a>
Modifier un en-tête <code>Cookie</code> ou <code>Set-Cookie</code> et contrôler SameSite ou le préfixe <code>__Host-</code> : l’<a href="/fr/cookie-header-editor" class="text-primary hover:underline">éditeur de cookies</a>
Lire les claims d’un jeton Bearer trouvé dans un en-tête Authorization : le <a href="/fr/jwt-decode" class="text-primary hover:underline">décodeur JWT</a>, et le <a href="/fr/blog/encoder-decoder-jwt-en-ligne" class="text-primary hover:underline">guide JWT</a> pour ce qu’un jeton décodé prouve ou non
Valider le contrat d’API plutôt qu’un appel isolé : le <a href="/fr/validate-openapi" class="text-primary hover:underline">validateur OpenAPI</a> et le <a href="/fr/blog/valider-openapi-swagger-en-ligne" class="text-primary hover:underline">guide de validation OpenAPI</a>

Déboguer un appel d’API à partir d’une requête copiée

Ça marche dans le navigateur, pas dans votre script

Collez le Copy as cURL dans le formateur cURL et lisez-le ligne par ligne : il contient tout ce que le navigateur a ajouté, pas seulement ce dont votre code a besoin.

Les en-têtes ajoutés par le navigateur comme <code>sec-ch-ua</code>, <code>accept-language</code>, <code>origin</code>, <code>referer</code> et toute la ligne <code>cookie</code> sont rarement indispensables. Retirez-les un par un jusqu’à ce que l’appel échoue : le dernier retiré est celui qui comptait
Un appel authentifié par cookie échoue depuis un script parce que le cookie manque, pas parce que le code est faux : la session vit dans <code>Cookie</code>, alors qu’un client d’API envoie plutôt un en-tête <code>Authorization</code>
La méthode est déduite quand elle n’est pas indiquée : un corps sans <code>-X</code> donne POST, pas de corps donne GET. Un GET qui porte un corps signale qu’un morceau s’est perdu dans la copie
Un <code>Content-Type</code> qui ne correspond pas au corps (du JSON envoyé en <code>application/x-www-form-urlencoded</code>, ou l’inverse) est la cause classique d’un 400 ou d’un 415
Un <code>--compressed</code> final signifie que le navigateur a envoyé <code>Accept-Encoding</code> et décodé la réponse à votre place. Votre client doit faire de même, sinon le corps ressemble à du charabia
Lire une réponse : statut, en-têtes, corps

Collez la réponse dans le parseur de message HTTP. La ligne de statut, le tableau d’en-têtes et le corps sont présentés séparément.

La classe du statut dit qui soupçonner en premier : 4xx pour la requête, 5xx pour le serveur, 3xx pour une redirection à suivre (RFC 9110). Sur un 3xx, regardez <code>Location</code>
<code>WWW-Authenticate</code> sur un 401 nomme le schéma attendu par le serveur, et <code>Retry-After</code> sur un 429 ou un 503 indique combien de temps attendre
Les en-têtes en double restent sur des lignes séparées. Deux lignes <code>Set-Cookie</code> sont deux cookies, pas une erreur, et un <code>Vary</code> répété est légal
Le Content-Type décide de la façon de lire le corps : une erreur JSON servie en <code>text/html</code> vient en général d’un proxy ou d’une passerelle, pas de votre API
Les corps en transfert fragmenté ne sont pas réassemblés. Si le corps commence par une taille hexadécimale seule sur sa ligne, vous avez collé le flux brut ; copiez plutôt le corps décodé depuis les DevTools
Retrouver l’appel en échec dans un HAR

Ouvrez l’export dans l’inspecteur HAR et réduisez la liste avant de lire le moindre détail.

Les compteurs Requêtes, Taille, Durée et Erreurs se trouvent au-dessus de la liste. Erreurs compte les entrées dont le statut est 400 ou plus
Une requête qui n’a reçu aucune réponse est enregistrée avec le statut 0 (bloquée par une extension, échec DNS, préflight CORS refusé). Elle n’est pas comptée comme une erreur : parcourez aussi la liste non filtrée
La pastille XHR regroupe les appels fetch et XMLHttpRequest. Ajoutez un filtre texte sur <code>/api/</code> pour écarter polices, images et scripts de suivi
Sélectionnez une ligne pour lire les en-têtes de requête, ceux de la réponse, le payload et un aperçu du corps de réponse, puis Copier cURL pour la rejouer
Un HAR exporté depuis Chrome 130 ou plus récent est masqué par défaut : il omet Cookie, Set-Cookie et Authorization. Un en-tête Authorization absent du fichier ne prouve pas que le navigateur ne l’a pas envoyé

Quatre erreurs qui font fuiter un identifiant ou vous égarent

Partager une requête copiée avec ses identifiants

Un Copy as cURL issu d’une session authentifiée contient vos en-têtes Cookie et Authorization : quiconque lit le ticket peut rejouer l’appel à votre place. L’interrupteur Retirer les secrets retire de la copie Cookie, Authorization, Referer, sec-ch-* et les valeurs de -u et -b. Il est désactivé par défaut dans le formateur et dans le Copy as cURL du parseur, et activé par défaut dans l’inspecteur HAR. Il ne traite que les en-têtes : un jeton dans la chaîne de requête (?access_token=), dans le corps, dans un en-tête personnalisé comme X-Api-Key ou dans le corps d’une réponse reste exactement où il était. Relisez la sortie avant de la coller et changez tout ce qui est déjà sorti de votre machine. Le détecteur de secrets aide à repérer une clé que vous n’aviez pas vue.

Poster un Copy as cURL dans une issue publique parce que l’en-tête Authorization n’est « qu’un jeton de test »
Croire que Retirer les secrets retire aussi les jetons de l’URL ou du corps
Envoyer un HAR à un prestataire sans vérifier les corps de réponse, qui peuvent contenir des données personnelles
Laisser une clé longue durée dans un cURL conservé dans un wiki ou un historique de shell
Retirez les secrets, puis relisez le résultat : chaînes de requête, corps et en-têtes personnalisés restent à nettoyer à la main. Renouvelez tout identifiant qui a été partagé.
Lire les compteurs du HAR comme la performance de la page

La Durée de l’inspecteur HAR est celle de l’entrée la plus longue, pas le temps de chargement de la page, et Taille additionne les tailles de corps enregistrées dans chaque entrée. Ils répondent à « quel appel a été le plus lent » et « combien de données ont circulé », pas à ce que mesure Lighthouse. Les requêtes s’exécutent en parallèle : la somme des durées n’est pas non plus le temps de la page. Les réponses en cache et les corps compressés font différer les tailles de celles affichées au pied du panneau Réseau.

Citer la Durée de l’inspecteur comme temps de chargement de la page
Comparer la taille totale du HAR au poids transféré de Lighthouse et déclarer l’un des deux faux
Ignorer les requêtes de statut 0 parce que le compteur Erreurs affiche 0
Juger un problème de performance sur un seul HAR enregistré avec le cache désactivé et une extension active
Vous optimisez le mauvais endpoint, ou vous annoncez un taux d’erreur nul alors qu’une requête est bloquée en silence.
Attendre une relecture fidèle de ce que vous avez collé

Le formateur comprend le sous-ensemble de cURL produit par les DevTools et ignore le reste sans le dire. Des options comme -L, -s, -k, --insecure, -v et -o disparaissent de la sortie : une commande qui comptait sur -L pour suivre une redirection ou sur -k pour ignorer le certificat perd ce comportement à la traduction. Certaines entrées sont refusées avec un message clair : envois multipart (-F), fichiers -T, corps @fichier, {{variables}} de REST Client, fichiers .http à plusieurs requêtes, snippets axios, appels fetch avec gabarits de chaîne ou variables non liées. Une réponse collée n’est pas une requête : il n’y a rien à transformer en cURL.

Traduire un cURL qui utilisait -L en attendant du fetch généré qu’il se comporte pareil
Coller un envoi multipart et s’étonner que le formateur le refuse
Vouloir copier en cURL à partir d’un collage qui ne contient qu’une réponse
Traiter le snippet généré comme du code de production plutôt que comme un point de départ
Relisez une fois le code généré, ajoutez vous-même délais, nouvelles tentatives et gestion d’erreurs, et gardez les secrets dans des variables d’environnement.
Modifier le corps sans s’en apercevoir

Plusieurs sorties ré-indentent un corps JSON : le cURL multiligne, les sorties HTTP/1.1 et .http, le fetch JavaScript, et Python requests, qui le transforme en littéral json=. Le cURL sur une ligne garde le corps tel que collé. C’est sans conséquence pour la plupart des API, mais fatal quand le serveur vérifie une signature calculée sur les octets exacts, comme le font souvent les points d’entrée de webhooks : un seul espace ajouté et le HMAC ne correspond plus. Le guide du hash et du HMAC explique comment ces signatures sont construites.

Rejouer un corps de webhook signé après son passage dans un embellisseur
Comparer un snippet généré à l’original octet par octet
Utiliser la sortie multiligne comme fixture d’un test de signature
Coller la sortie multiligne dans un test qui compare le corps comme une chaîne
Pour un corps signé, gardez le texte d’origine, utilisez le cURL sur une ligne et calculez la signature sur la chaîne exacte que vous envoyez.

Anatomie d’un message HTTP et ce que regarde chaque outil

Ligne de départ, en-têtes, ligne vide, corps

Un message HTTP/1.1 est du texte brut (RFC 9112) dont le sens est défini par la RFC 9110. Requête et réponse partagent la même disposition.

Ligne de requête : <code>POST /orders HTTP/1.1</code> porte la méthode, la cible et la version du protocole. Une requête doit comporter un en-tête <code>Host</code>
Ligne de statut : <code>HTTP/1.1 201 Created</code> porte la version, un code à trois chiffres et une phrase explicative que les clients ignorent
En-têtes : un <code>Nom: valeur</code> par ligne, noms insensibles à la casse, et un même nom peut apparaître plusieurs fois
Une ligne vide termine les en-têtes. Ce qui suit est le corps, qui peut être vide (un GET, un 204)
Les lignes d’en-tête repliées (une suite qui commence par une espace) sont jointes en une seule valeur par le parseur
La même requête sous quatre formes

Les outils convertissent de l’une à l’autre, d’où le fait qu’une requête copiée puisse devenir n’importe laquelle.

Copy as cURL : <code>curl URL -H "Nom: valeur" --data-raw CORPS --compressed</code>, la méthode étant ajoutée par <code>-X</code> quand ce n’est pas GET
Copy as fetch : <code>fetch(url, { method, headers, body })</code>, un littéral JavaScript que le formateur lit comme du texte et n’évalue jamais
HTTP/1.1 brut : la ligne de requête, un en-tête Host et les autres en-têtes, puis le corps. Le format <code>.http</code> des extensions REST Client est identique, avec l’URL complète sur la première ligne
Entrée HAR : un objet JSON avec <code>request</code> (method, url, headers, postData) et <code>response</code> (status, headers, content), plus un <code>time</code> en millisecondes. Le HAR 1.2 est décrit dans un brouillon de spécification du W3C
Les sorties Python, Go et PHP sont le même appel écrit avec requests, net/http et l’extension curl
Classes de statut et en-têtes à lire en premier

Un court ordre de lecture évite la plupart des séances de débogage interminables.

1xx information, 2xx succès, 3xx redirection, 4xx erreur du client, 5xx erreur du serveur (RFC 9110). Le parseur affiche la classe d’une réponse sous forme de pastille colorée
401 signifie « non authentifié » et s’accompagne de <code>WWW-Authenticate</code> ; 403 signifie authentifié mais non autorisé ; 429 signifie limite de débit atteinte et porte souvent <code>Retry-After</code>
<code>Content-Type</code> et <code>Content-Length</code> (ou le transfert fragmenté) décrivent le corps, <code>Accept</code> et <code>Accept-Encoding</code> ce que le client sait lire
<code>Cache-Control</code>, <code>ETag</code> et <code>Age</code> expliquent pourquoi une réponse est périmée ou fraîche, et <code>Set-Cookie</code> et <code>Location</code> changent ce que fait ensuite le client
Les mêmes classes servent de filtre Statut dans l’inspecteur HAR, avec un raccourci Erreurs pour 400 et plus

Les trois outils, pas à pas

Formater un Copy as cURL avec le formateur cURL

Le formateur cURL reformate au fil du collage, sans bouton Envoyer. Dans Chrome, faites un clic droit sur la requête dans l’onglet Réseau, puis Copier, puis Copy as cURL (bash) (libellé traduit selon la langue de Chrome).

1

Collez la commande

Un Copy as cURL, un Copy as fetch, un fichier .http à requête unique ou une requête HTTP/1.1 brute sont détectés. Les retours à la ligne avec continuation sont joints pour vous.

2

Choisissez le langage et la disposition

cURL, HTTP/1.1, .http, fetch JavaScript, Python requests, Go net/http ou PHP curl, en disposition multiligne ou sur une ligne.

3

Activez Retirer les secrets avant de partager

Il retire de la copie Cookie, Authorization, Referer, sec-ch-* et les valeurs de -u et -b. Chaînes de requête et corps restent intacts.

4

Copiez ou téléchargez le résultat

Chaque langage a son nom de fichier (formatted-curl.sh, formatted-request.http, formatted-requests.py, etc.). Une erreur d’analyse dit ce qui a été refusé et pourquoi.

Lire une requête ou une réponse brute avec le parseur de message HTTP

Le parseur de message HTTP présente un message collé sous forme de ligne de départ, de tableau d’en-têtes et de corps.

1

Collez le message

Une requête, une réponse, un bloc d’en-têtes seuls ou la sortie de curl -v. Les fins de ligne LF et CRLF fonctionnent toutes deux.

2

Vérifiez le type détecté

L’outil l’étiquette requête HTTP, réponse HTTP ou en-têtes seuls, puis affiche le nombre d’en-têtes. Les doublons restent sur des lignes distinctes.

3

Lisez le corps

Un corps JSON est mis en forme par défaut, avec un interrupteur pour le voir brut. Les corps fragmentés ne sont pas réassemblés.

4

Copiez une requête en cURL

Il faut une requête avec une URL absolue ou un en-tête Host. Avec le seul Host, le schéma est deviné : http pour localhost, 127.0.0.1 ou le port 80, https sinon. L’interrupteur Assainir la copie est désactivé par défaut.

Explorer une session avec l’inspecteur HAR

L’inspecteur HAR lit le fichier dans votre onglet et ne va jamais chercher les URL qu’il liste.

1

Exportez le HAR

Dans le panneau Réseau, utilisez l’icône de téléchargement (Export HAR). Les exports de Chrome, Firefox, Edge, Charles et HTTP Toolkit s’ouvrent jusqu’à 50 Mo ; au-delà de 20 Mo, le filtrage peut être lent.

2

Déposez le fichier ou collez le JSON

Un fichier .har ou .json, ou le JSON HAR 1.2 lui-même. Le bouton d’exemple charge une petite capture pour essayer les filtres.

3

Réduisez la liste

Pastilles de type (XHR, JS, CSS, Img, Media, Font, Doc, Autre, Erreurs), méthode, classe de statut et recherche « URL contient ». Le type vient de l’entrée quand Chrome l’enregistre, ou du type MIME de la réponse.

4

Ouvrez un appel et extrayez-le

Lisez les en-têtes de requête et de réponse, le payload et l’aperçu du corps. Copier cURL, Copier HTTP/1.1 ou Copier l’URL, ou téléchargement du fichier : avec Sanitizer les secrets activé, il est écrit sous le nom sanitized.har, sans les en-têtes Cookie, Set-Cookie, Authorization, Referer et sec-ch-* ni les tableaux de cookies.

Ce que les outils ne font pas

Quelques limites à connaître avant de s’y fier.

Aucune requête n’est jamais envoyée ni rejouée : pas d’exécuteur, pas de proxy, pas de bouton Envoyer
Pas de corps multipart (-F) ni de fichier (-T, @fichier), pas de {{variables}} REST Client, pas de snippets axios, pas de fichiers .http à plusieurs requêtes
Les options cURL comme -L, -k, -s et -o sont ignorées dans la sortie
Pas de réassemblage des corps fragmentés dans le parseur de message
L’inspecteur HAR ne dessine pas de cascade (waterfall) et ne calcule pas les temps de la page
Retirer les secrets agit sur les en-têtes et les tableaux de cookies, pas sur les URL, les corps ni les en-têtes personnalisés

Les mêmes vérifications dans un terminal et en Node

curl : les en-têtes seuls, ou tout l’échange

Quand vous pouvez lancer une commande, curl montre les mêmes données que celles que présente le parseur. Gardez les jetons dans des variables d’environnement, pas dans la commande que vous collez dans un ticket.

Exemple de base

# Response headers only: -D - writes them to stdout, -o /dev/null drops the body curl -sS -D - -o /dev/null https://api.example.com/orders/42 \ -H "Authorization: Bearer $API_TOKEN" # The full exchange: lines starting with > are sent, < received, * is curl's own log curl -v https://api.example.com/orders/42 2>&1 | head -40
Node : filtrer un HAR sans ouvrir de navigateur

Un HAR est du JSON : quelques lignes suffisent pour lister les appels en échec. Contrairement au compteur Erreurs de l’inspecteur, ce filtre garde aussi le statut 0.

Exemple de base

import { readFileSync } from 'node:fs' const har = JSON.parse(readFileSync('session.har', 'utf8')) // Status 0 means no response at all (blocked, DNS failure, CORS): keep it in the list const failed = har.log.entries .filter((e) => e.response.status >= 400 || e.response.status === 0) .map((e) => ({ method: e.request.method, status: e.response.status, url: e.request.url, ms: Math.round(e.time), })) console.table(failed)
Node : reproduire un appel fetch copié

Prenez la sortie de Copy as fetch, déplacez le secret dans une variable d’environnement et affichez ce qui revient. Les snippets générés par le formateur sont un point de départ du même genre.

Exemple de base

// The call you copied as fetch(), with the secret read from the environment const res = await fetch('https://api.example.com/orders', { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ' + process.env.API_TOKEN, }, body: JSON.stringify({ sku: 'A-1042', qty: 2 }), }) console.log(res.status, res.statusText) console.log(res.headers.get('content-type')) console.log(res.headers.getSetCookie()) // every Set-Cookie line, Node 20+ console.log(await res.text())
Les outils HTTP de FastMinify

Rien à installer et rien d’envoyé : le formateur cURL, le parseur de message HTTP et l’inspecteur HAR couvrent la lecture, la traduction et le partage d’une requête. Les compromis : pas de rejeu, un sous-ensemble de cURL, pas de cascade des temps, et un nettoyage qui ne connaît que les en-têtes. Pour un vrai test, lancez l’appel dans un terminal, Postman ou votre propre suite de tests ; pour vérifier une spécification plutôt qu’un appel, utilisez le validateur OpenAPI.

Conclusion

Un échange HTTP n’est que du texte, et déboguer consiste surtout à le lire sous la bonne forme. Le formateur cURL rend un Copy as cURL lisible et portable, le parseur de message HTTP met en page une requête ou une réponse brute avec ses en-têtes en double et son corps JSON, et l’inspecteur HAR réduit une session enregistrée à l’appel qui a échoué. Aucun n’envoie quoi que ce soit : vous gardez la main sur ce qui quitte votre navigateur, ce qui compte puisque ces collages contiennent des cookies et des jetons. Retirez les secrets avant de partager, rappelez-vous que cela ne nettoie que les en-têtes, et lisez les compteurs pour ce qu’ils sont. Le hub des outils HTTP réunit les outils voisins pour les URL et les cookies.

Lisez un Copy as cURL ligne par ligne et retirez les en-têtes ajoutés par le navigateur avant de reproduire un appel
Retirez les secrets avant de partager, puis nettoyez à la main la chaîne de requête, le corps et les en-têtes personnalisés
Dans un HAR, comptez le statut 0 comme un échec et lisez la Durée comme l’appel le plus lent, pas comme le temps de la page
Gardez le corps d’origine quand une signature le couvre : ré-indenter du JSON change les octets
Rejouez un appel depuis un terminal ou une suite de tests : les outils du navigateur lisent et traduisent, ils n’envoient jamais
Partager cet article
Partager cet article: