Il y a des outils qu'on installe sans y penser, qu'on utilise sans les comprendre, et qu'on maudit sans raison valable. curl est de ceux-là. Présent sur quasiment tous les systèmes Unix depuis 1998, il tourne sur vos serveurs, vos conteneurs, vos frigos connectés, vos voitures, et d'après Daniel Stenberg lui-même, probablement sur des équipements médicaux que vous préféreriez ne pas imaginer. C'est dire.
Un peu d'histoire
Son créateur a écrit curl dans son coin en 1996 parce qu'il voulait télécharger des cours de change pour un bot IRC. Ce n'est pas une blague. L'outil que vous utilisez pour débugger vos webhooks en prod a été écrit pour un bot de chat qui suivait le cours du dollar. L'informatique est une discipline sérieuse.
Ce que curl fait, concrètement
curl transfère des données. C'est tout. Vers un serveur, depuis un serveur, en HTTP, HTTPS, FTP, SFTP, SMTP, et une vingtaine d'autres protocoles que vous n'utiliserez jamais mais qui sont là, quelque part, à attendre patiemment comme un stagiaire en juillet.
La commande que tout le monde connaît :
curl https://example.com
Ce que ça fait : une requête GET, le résultat dans le terminal, et l'impression de maîtriser l'outil. Vous n'en êtes qu'aux prémices.
Les options curl essentielles
-v - verbose
curl -v https://api.example.com
Affiche tout - la négociation TLS, les headers envoyés, les headers reçus, le code de réponse. C'est verbeux, c'est parfois illisible, et c'est exactement ce qu'il vous faut quand quelque chose ne fonctionne pas. Si vous n'utilisez pas -v en premier réflexe de debug, vous cherchez votre clé sous le lampadaire parce que c'est là qu'il y a de la lumière.
-I - headers only
curl -I https://example.com
Envoie une requête HEAD et affiche uniquement les headers. Pratique pour vérifier un Content-Type, un cache-control, ou si votre CDN fait vraiment ce que vous pensez qu'il fait. Spoiler : souvent non.
-L - follow redirects
curl -L https://example.com
Suit les redirections automatiquement. Par défaut curl ne le fait pas, ce qui surprend tout le monde la première fois. Voilà pourquoi votre requête retourne un 301 au lieu du contenu attendu depuis dix minutes.
-o - output vers un fichier
curl -o resultat.json https://api.example.com/data
Écrit le résultat dans un fichier plutôt que dans le terminal. Évite de se retrouver avec 3000 lignes de JSON dans l'historique que vous ne retrouverez plus jamais.
-s et -S - le duo silencieux
curl -s -S https://api.example.com | jq .
-s supprime la barre de progression. -S affiche quand même les vraies erreurs. Les deux ensemble : silencieux mais pas aveugle. Indispensable quand vous pipez vers jq.
-w - format de sortie personnalisé
curl -o /dev/null -s \
-w "dns: %{time_namelookup}s\ntcp: %{time_connect}s\ntls: %{time_appconnect}s\ntotal: %{time_total}s\n" \
https://example.com
La variable la plus utile que personne n'utilise. Vous saurez exactement où votre requête perd du temps - DNS, TCP, TLS, ou l'API qui réfléchit trop.
--retry
curl --retry 3 --retry-delay 2 --retry-all-errors https://api.example.com
Réessaie automatiquement en cas d'échec réseau. --retry-all-errors étend ça aux erreurs HTTP. Parce que les APIs tombent, les réseaux flanchent, et écrire une boucle bash autour de curl c'est du temps que vous pourriez consacrer à autre chose.
--compressed
curl --compressed https://api.example.com
Demande une réponse compressée et la décompresse automatiquement. Si votre API supporte gzip - et elle devrait - vous transférez moins de données. Personne ne le fait. Personne ne sait pourquoi.
Les cas d'usage DevOps
Tester un endpoint avec authentification
curl -H "Authorization: Bearer $TOKEN" \
-H "Content-Type: application/json" \
https://api.example.com/endpoint
Le $TOKEN en variable d'environnement, pas en dur dans la commande. Votre historique bash est lisible par n'importe qui ayant accès à votre session. Votre RSSI vous remercie, même s'il ne le dira jamais.
Envoyer du JSON en POST
curl -X POST \
-H "Content-Type: application/json" \
-d '{"service":"radar","version":"1.2.0","env":"prod"}' \
https://api.example.com/deploy
Ou depuis un fichier, ce qui évite les problèmes d'échappement de caractères spéciaux dans le shell :
curl -X POST \
-H "Content-Type: application/json" \
-d @payload.json \
https://api.example.com/deploy
Mesurer les performances sous charge
for i in {1..10}; do
curl -o /dev/null -s -w "%{time_total}\n" https://example.com
done | awk '{sum+=$1; count++} END {printf "moy: %.3fs\n", sum/count}'
Dix requêtes consécutives avec la moyenne. Beaucoup plus représentatif qu'une seule mesure. Si la variance est élevée, vous avez un problème de cohérence - cache qui rate, autoscaling qui démarre, connexions qui ne sont pas réutilisées.
Débugger un timeout réseau
curl --connect-timeout 5 --max-time 10 -v https://service-lent.example.com
--connect-timeout : temps max pour établir la connexion TCP. --max-time : temps total de la requête. Si ça échoue sur --connect-timeout, le service est inaccessible. Si ça échoue sur --max-time, il répond mais trop lentement. La distinction change complètement le diagnostic.
Tester derrière un proxy d'entreprise
curl -x http://proxy.corp.example.com:3128 \
-U username:password \
https://api.externe.com
Cas très courant en entreprise, rarement documenté correctement. Si votre proxy utilise NTLM : ajoutez --proxy-ntlm. Si les certificats d'inspection TLS posent problème : --proxy-cacert /chemin/vers/ca-corp.crt.
Authentification mTLS
curl --cert client.crt \
--key client.key \
--cacert ca.crt \
https://api-securisee.example.com
De plus en plus courant dans les architectures zero trust et les communications service-to-service. Si votre certificat client est dans un fichier PEM combiné : --cert combined.pem. Si le serveur exige un certificat et que vous n'en fournissez pas, vous aurez un SSL handshake failed sans explication utile. Maintenant vous savez.
Rejouer une requête avec headers personnalisés
curl -H "X-Forwarded-For: 203.0.113.42" \
-H "X-Real-IP: 203.0.113.42" \
-H "X-Custom-Header: test-value" \
https://api.example.com/endpoint
Pour tester du rate limiting par IP, du geo-blocking, ou vérifier que votre middleware lit bien les bons headers. À utiliser avec discernement - certains services considèrent ça comme une tentative de contournement, à juste titre.
En pratique
$ curl -v https://api.example.com
* Trying 93.184.216.34:443...* Connected to api.example.com port 443* ALPN: curl offers h2,http/1.1* TLSv1.3 (OUT), TLS handshake, Client hello* TLSv1.3 (IN), TLS handshake, Server hello* TLSv1.3 (IN), TLS handshake, Encrypted Extensions* TLSv1.3 (IN), TLS handshake, Certificate* TLSv1.3 (IN), TLS handshake, ALPN extension* TLSv1.3 (IN), TLS handshake, Finished* SSL connection using TLSv1.3 / TLS_AES_256_GCM_SHA384* Server certificate: api.example.com* expire date: Oct 15 2026 00:00:00 GMT* SSL certificate verify ok.* using HTTP/2> GET / HTTP/2> Host: api.example.com> User-Agent: curl/8.6.0> Accept: */*>< HTTP/2 200< content-type: application/json< cache-control: no-cache< x-request-id: 7f3a2b1c-9d4e-4f8a-b6c2< content-length: 54<{"status":"ok","version":"2.1.4","env":"production"}
Erreurs curl courantes à éviter
curl | bash - le grand classique
# Ne faites pas ça
curl https://script-pas-lu.sh | bash
Exécute directement ce que curl télécharge, sans que vous l'ayez lu. C'est pratique, c'est rapide, et c'est la façon la plus naze de compromettre votre machine si vous faites confiance au mauvais serveur ou si le DNS est poisonné entre le moment où vous vérifiez et le moment où vous exécutez. Téléchargez d'abord, lisez ensuite. Si vous ne comprenez pas ce que le script fait, c'est un signe.
-k - ignorer les certificats SSL
# En dev pourquoi pas. En prod : non.
curl -k https://service-avec-cert-invalide.example.com
Désactive la vérification SSL. Règle le problème immédiatement et en crée un plus grand silencieusement. Si le certificat est invalide en prod, la bonne réponse c'est de le corriger, pas de désactiver la vérification. J'ai vu des -k en prod oubliés pendant 8 mois. La personne concernée se reconnaîtra.
Les variables d'environnement qui traînent
curl lit http_proxy, https_proxy, no_proxy depuis l'environnement. Si vos requêtes partent dans une direction inattendue, vérifiez vos variables d'env avant de passer deux heures à blâmer le réseau ou votre collègue.
Les guillemets dans le shell
# Échoue silencieusement sur certains shells
curl -d "{"key": "value"}" https://api.example.com
# Ce qu'il faut faire
curl -d '{"key": "value"}' https://api.example.com
# Ou mieux
curl -d @payload.json https://api.example.com
L'interpolation des guillemets en bash est une source infinie de bugs discrets. Quand votre payload JSON ne passe pas correctement, commencez par là.
Alternatives à curl : HTTPie, wget, Postman, Bruno
HTTPie - http GET https://example.com. Syntaxe plus lisible, output coloré, JSON automatique. Excellent pour l'usage interactif. Absent par défaut sur tous les serveurs que vous debuggerez en urgence à 3h du matin. C'est son principal défaut.
wget - Le concurrent historique. Meilleur pour télécharger des fichiers récursivement, moins bon pour tout le reste. Présent sur plus de systèmes que HTTPie, moins flexible que curl.
Postman / Insomnia - Des GUI pour tester des APIs. Utiles pour explorer, moins utiles dans un script, inutiles en SSH sur un serveur de prod sans interface graphique.
Bruno - Mon chouchou. Client API open source et local-first : vos collections restent dans des fichiers versionnables, pas de compte, pas de cloud, pas d'abonnement qui double de prix le jour où vous ne regardez plus. J'en ai parlé dans la newsletter du lundi 2 juin 2025, que le temps passe vite.
curl gagne parce qu'il est partout, tout le temps, sans installation. Quand vous êtes sur un conteneur Alpine de 5MB à 3h du matin, c'est curl qui est là. Pas HTTPie, pas Postman. curl.
La config ~/.curlrc
curl lit ce fichier au démarrage. Mettez-y vos options par défaut et n'y pensez plus :
# ~/.curlrc
silent
show-error
location
compressed
Désormais chaque curl est silencieux, suit les redirections, demande de la compression, et affiche quand même les vraies erreurs. Sans taper les options à chaque fois. C'est petit, c'est bête, c'est efficace.
À retenir
| Commande | Pour quoi |
|---|---|
curl -v URL | Voir tout ce qui se passe |
curl -I URL | Headers seulement |
curl -L URL | Suivre les redirections |
curl -o file URL | Sauvegarder dans un fichier |
curl -s -S URL | jq . | JSON silencieux mais avec erreurs |
curl --retry 3 URL | Réessayer en cas d'échec |
curl --connect-timeout 5 --max-time 10 URL | Débugger les timeouts |
curl --cert c.crt --key c.key URL | Authentification mTLS |
FAQ
Comment récupérer uniquement le code HTTP de la réponse ?
curl -o /dev/null -s -w "%{http_code}" https://example.com
-o /dev/null jette le body, -s supprime la progression, -w "%{http_code}" affiche uniquement le code de retour. Utile dans les scripts pour brancher sur le code sans parser le body.
Comment passer plusieurs headers ?
Répétez -H autant de fois que nécessaire :
curl -H "Authorization: Bearer $TOKEN" \
-H "Accept: application/json" \
-H "X-Request-ID: $(uuidgen)" \
https://api.example.com
Mon `~/.curlrc` est ignoré - pourquoi ?
Deux causes courantes : la variable $CURL_HOME est définie et pointe ailleurs (echo $CURL_HOME), ou les options sont précédées de -- dans le fichier alors qu'elles ne le devraient pas. Dans .curlrc, écrivez location et non --location.
Pour aller plus loin
- Everything curl - le livre officiel de Daniel Stenberg, gratuit, exhaustif
man curl- 4000 lignes. La réponse à votre question est dedans. Oui, même celle-là.