Guide

curl L'outil que vous utilisez mal depuis 20 ans

curl est sur quasiment tous vos serveurs et vous ne l'utilisez qu'à 10% de ses capacités. Options essentielles, cas d'usage DevOps, pièges à éviter.

Sommaire

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

terminalrésultats simulés
$ 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

CommandePour quoi
curl -v URLVoir tout ce qui se passe
curl -I URLHeaders seulement
curl -L URLSuivre les redirections
curl -o file URLSauvegarder dans un fichier
curl -s -S URL | jq .JSON silencieux mais avec erreurs
curl --retry 3 URLRéessayer en cas d'échec
curl --connect-timeout 5 --max-time 10 URLDébugger les timeouts
curl --cert c.crt --key c.key URLAuthentification 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à.