Codes HTTP : aide-mémoire complet (1xx-5xx) expliqués
Vous ouvrez les DevTools et l’onglet Network est à moitié rouge. Votre endpoint renvoie 502 en production, 200 en local, et un collègue vient de demander sur Slack : « ça doit renvoyer 401 ou 403 ? ». Les codes HTTP paraissent simples (trois chiffres, cinq familles), mais le mauvais choix divulgue des informations, casse le SEO et transforme les astreintes en cauchemar.
Ce guide est un aide-mémoire des codes HTTP pour les développeurs en activité. Vous y trouverez un tableau de référence rapide de tous les codes que vous croisez en production, des matrices de décision pour les paires que tout le monde confond (301 vs 302, 401 vs 403, 404 vs 410, 502 vs 504), et une section outillage qui montre comment inspecter les codes de statut depuis curl, fetch et Python requests. Chaque code ci-dessous s’appuie sur la RFC 9110, la norme actuelle de la sémantique HTTP, et sur le registre IANA des codes de statut HTTP.
Référence rapide : tous les codes HTTP en un coup d’œil
Voici les codes que vous rencontrerez en production, regroupés par classe. Mettez ce tableau en favori. La suite de l’article explique les codes les plus délicats.
| Code | Nom | Quand vous le verrez |
|---|---|---|
| 100 | Continue | Envoi d’un gros corps POST avec Expect: 100-continue |
| 101 | Switching Protocols | Handshake WebSocket, upgrade HTTP/2 |
| 103 | Early Hints | Le serveur pousse des en-têtes Link avant la vraie réponse |
| 200 | OK | Succès par défaut pour GET, PUT, PATCH |
| 201 | Created | POST qui crée une ressource (renvoie Location) |
| 202 | Accepted | Job asynchrone mis en file, pas encore exécuté |
| 204 | No Content | Succès d’un DELETE, PUT sans corps à renvoyer |
| 206 | Partial Content | Range request, lecture vidéo avec saut, téléchargement reprenable |
| 301 | Moved Permanently | Ancienne URL retirée, les moteurs transfèrent le PageRank |
| 302 | Found | Redirection temporaire, l’URL d’origine reste canonique |
| 303 | See Other | Pattern Post/Redirect/Get après un POST de formulaire |
| 304 | Not Modified | GET conditionnel avec ETag ou If-Modified-Since correspondant |
| 307 | Temporary Redirect | Comme 302, mais méthode et corps préservés |
| 308 | Permanent Redirect | Comme 301, mais méthode et corps préservés |
| 400 | Bad Request | JSON malformé, champ requis manquant, validation de schéma échouée |
| 401 | Unauthorized | Pas de credentials ou token expiré |
| 403 | Forbidden | Authentifié mais non autorisé |
| 404 | Not Found | La ressource n’existe pas (ou vous la cachez) |
| 405 | Method Not Allowed | POST sur un endpoint en GET seul (doit inclure Allow) |
| 408 | Request Timeout | Le client a mis trop de temps à envoyer la requête |
| 409 | Conflict | Échec de verrou optimiste, clé en doublon |
| 410 | Gone | Ressource supprimée définitivement, ne reviendra pas |
| 415 | Unsupported Media Type | Mauvais Content-Type, ex. XML envoyé à une API JSON |
| 422 | Unprocessable Content | Syntaxe valide, sémantique invalide (erreur de validation) |
| 425 | Too Early | Risque de rejeu de données précoces TLS 1.3 |
| 428 | Precondition Required | Le serveur exige If-Match pour éviter les pertes de mises à jour |
| 429 | Too Many Requests | Rate limited (doit inclure Retry-After) |
| 451 | Unavailable for Legal Reasons | DMCA, retrait RGPD, geo-block |
| 500 | Internal Server Error | Exception non gérée dans votre code |
| 501 | Not Implemented | Méthode ou fonctionnalité non prise en charge (rare en REST) |
| 502 | Bad Gateway | L’upstream a renvoyé une réponse invalide |
| 503 | Service Unavailable | Mode maintenance ou surcharge |
| 504 | Gateway Timeout | L’upstream n’a pas répondu à temps |
| 507 | Insufficient Storage | WebDAV à court de disque |
| 508 | Loop Detected | Redirection ou récursion infinie en WebDAV |
| 511 | Network Authentication Required | Captive portal d’un WiFi d’hôtel ou d’aéroport |
Le reste de l’article décortique chaque classe avec des matrices de décision, des anti-patterns et les conséquences SEO d’un mauvais choix.
Comment fonctionnent les codes HTTP (anatomie à 3 chiffres)
Pourquoi trois chiffres ?
Les codes HTTP comportent trois chiffres décimaux parce qu’HTTP/0.9 avait besoin d’un signal à largeur fixe, suffisamment court pour qu’un parser puisse brancher rapidement et suffisamment long pour laisser de la place à de nouveaux codes. Trois chiffres donnent 900 valeurs possibles (100–999), bien plus que nécessaire : le registre IANA n’en utilise qu’une soixantaine aujourd’hui.
Le premier chiffre indique la classe. Les deuxième et troisième indiquent le code spécifique au sein de cette classe. Un client qui ne reconnaît pas 418 doit le traiter comme un 4xx générique. La RFC 9110 §15 le rend explicite : les clients doivent traiter les codes inconnus comme le x00 de leur classe.
Les cinq catégories en un coup d’œil
| Classe | Signification | Corps requis ? | Cachable par défaut ? |
|---|---|---|---|
1xx | Informationnel : provisoire, la suite arrive | Non | Non |
2xx | Succès : la requête a été comprise et acceptée | Souvent | Dépend de la méthode |
3xx | Redirection : action supplémentaire requise | Optionnel | 301, 308 oui ; 302, 307 non |
4xx | Erreur client : votre faute, corrigez la requête | Oui (expliquer) | Généralement non |
5xx | Erreur serveur : notre faute, un retry peut aider | Oui (expliquer) | Non |
La colonne « cachable par défaut » compte beaucoup. Les CDN et les navigateurs cachent 301 et 308 agressivement et durablement. Choisir le mauvais code de redirection en production est très difficile à corriger après coup, parce que les utilisateurs ont déjà la redirection en cache local. Nous y reviendrons dans la section SEO.
Si vous voulez creuser la structure des URL (sur lesquelles les codes de redirection opèrent), le guide Encodage et décodage d’URL parcourt le pourcent-encodage, les query strings et la chaîne de traitement au niveau octet qui détermine ce qui rend une URL valide.
1xx Informationnel : quand vous les verrez vraiment
La plupart des développeurs passent des années sans voir un 1xx en direct. Ce sont des réponses provisoires : le serveur dit au client « je suis toujours là, continue ». Les DevTools du navigateur les masquent en général, et la plupart des bibliothèques HTTP les replient dans la réponse finale.
Pour chaque code ci-dessous, la référence des codes de statut HTTP de MDN reste la meilleure source de seconde lecture sur une définition.
100 Continue
Le client envoie Expect: 100-continue dans ses en-têtes et attend avant de transmettre un gros corps de requête. Le serveur répond 100 Continue s’il accepte de recevoir le corps, ou un 4xx s’il s’apprête à rejeter la requête. Cela économise de la bande passante sur les gros uploads : inutile d’envoyer 200 Mo si le serveur va rejeter la requête pour un en-tête manquant.
curl -v -H "Expect: 100-continue" \
-H "Content-Type: application/octet-stream" \
--data-binary @big-file.bin \
https://api.example.com/upload
Si vous ne voyez pas < HTTP/1.1 100 Continue dans la sortie verbose, votre client a probablement supprimé l’en-tête, ou le serveur ne le supporte pas.
101 Switching Protocols
Le handshake qui transforme une connexion HTTP en connexion WebSocket ou HTTP/2. Le client envoie Upgrade: websocket, le serveur répond 101 Switching Protocols, et la connexion parle ensuite un autre protocole. Vous le verrez dans l’onglet Network de toute application de chat ou de tout dashboard temps réel.
103 Early Hints
Code relativement récent (RFC 8297, 2017) qui permet au serveur d’envoyer des en-têtes Link pour des indices de preload avant que la réponse principale ne soit prête. Le navigateur commence à charger CSS et JS pendant que le serveur rend encore. En 2026, Cloudflare, Fastly et Vercel supportent tous 103 en production. C’est l’alternative moderne au server push HTTP/2, qui a été déprécié dans Chrome.
HTTP/1.1 103 Early Hints
Link: </styles.css>; rel=preload; as=style
Link: </app.js>; rel=preload; as=script
HTTP/1.1 200 OK
Content-Type: text/html
...
Vérification anti-pattern. Si votre client ne voit jamais les codes 1xx que vous attendez, le coupable est en général un reverse proxy. Les vieilles versions de nginx suppriment Expect: 100-continue et 103 Early Hints. Vérifiez la config de votre proxy avant de conclure que le serveur est cassé.
2xx Succès : au-delà du simple 200
Renvoyer 200 OK pour tout est le code-smell le plus courant dans les API REST. La famille 2xx porte des informations sémantiques qui rendent les clients plus intelligents et les caches plus efficaces.
200 OK
Le défaut. Un GET renvoie la ressource, un PUT renvoie la ressource mise à jour (ou 204), un PATCH renvoie la ressource patchée. Si vous n’avez pas de raison d’utiliser un code plus spécifique, utilisez 200.
201 Created
Un POST qui crée une nouvelle ressource doit renvoyer 201 plus un en-tête Location pointant vers la nouvelle ressource. C’est ainsi que les clients RESTful découvrent l’URL canonique de ce qu’ils viennent de créer.
HTTP/1.1 201 Created
Location: /api/users/42
Content-Type: application/json
{"id": 42, "name": "Ada Lovelace"}
202 Accepted
Le serveur a accepté la requête mais n’a pas fini de la traiter. À utiliser pour le travail asynchrone : le client doit poller, s’abonner à un webhook ou interroger un endpoint de statut. Associez-le à un job ID dans le corps.
204 No Content
Succès, pas de corps. Courant pour DELETE (la ressource est supprimée, qu’est-ce que vous renverriez ?) et pour les opérations PUT où le client connaît déjà le nouvel état. Les navigateurs ne changent pas la page courante si la soumission d’un formulaire renvoie 204, ce qui est utile pour les actions « fire-and-forget » dans les SPA.
206 Partial Content
Renvoyé pour les range requests : le client a demandé les octets 1000-2000 avec un en-tête Range: bytes=1000-2000, et le serveur a répondu juste avec cette tranche. Le streaming vidéo, les téléchargements reprenables et la synchronisation de fichiers via HTTP s’appuient tous sur 206.
Décision : 200 vs 201 vs 204 pour POST
| Scénario | Code | Corps |
|---|---|---|
| POST crée une nouvelle ressource | 201 Created | Nouvelle ressource (ou juste l’ID) + Location |
| POST déclenche un travail asynchrone, résultat pas prêt | 202 Accepted | Job ID, URL de polling |
POST est une action sans ressource (ex. /login) | 200 OK | Résultat de l’action (token, statut) |
| POST réussit mais la réponse est vide | 204 No Content | (aucun) |
Si vous hésitez entre 200 et 201, demandez-vous : « le serveur a-t-il créé une ressource qui possède maintenant sa propre URL ? ». Si oui, 201. Sinon, 200.
3xx Redirection : 301 vs 302 vs 307 vs 308
Les redirections sont la classe la plus mal utilisée. Les différences entre 301, 302, 307 et 308 se ramènent à trois questions orthogonales : le déplacement est-il permanent, la méthode est-elle préservée, et la réponse est-elle cachable.
301 Moved Permanently
La ressource a été déplacée et ne reviendra pas. Les moteurs de recherche transfèrent le PageRank à la nouvelle URL. Les navigateurs et les CDN cachent 301 indéfiniment : si vous redirigez /old vers /new en 301 puis changez d’avis, les utilisateurs avec une redirection en cache continueront d’aller sur /new pour toujours (ou jusqu’à ce qu’ils vident leur cache).
Historiquement, les navigateurs peuvent réécrire la méthode de la requête sur un 301 (POST en GET), ce qui est précisément la raison pour laquelle HTTP/1.1 a introduit 308.
302 Found
Redirection temporaire. L’URL d’origine reste canonique : les moteurs doivent continuer à indexer l’original. À utiliser pour le routage A/B, les pages de maintenance ou les flux « connectez-vous pour continuer ».
Comme avec 301, les navigateurs ont historiquement réécrit POST en GET sur 302. Si vous devez rediriger un POST en gardant POST, utilisez plutôt 307.
303 See Other
Réécrit toujours la méthode en GET. Le pattern Post/Redirect/Get : le formulaire fait un POST sur /submit, le serveur renvoie 303 avec Location: /thank-you, le navigateur fait un GET /thank-you. Rafraîchir la page de remerciement ne resoumet pas le formulaire. C’est exactement ce pour quoi 303 a été conçu.
304 Not Modified
La réponse conditionnelle. Le client envoie If-None-Match: "abc123" (ou If-Modified-Since), le serveur vérifie si la ressource a changé, et si non renvoie 304 sans corps. Le navigateur utilise sa copie en cache. C’est ainsi que tous les CDN et toutes les couches de cache gardent votre site rapide.
307 Temporary Redirect
Comme 302, sauf que la méthode ne doit pas changer. POST reste POST, le corps est préservé. À utiliser quand vous voulez une redirection temporaire sur une requête non-GET.
308 Permanent Redirect
Comme 301, sauf que la méthode ne doit pas changer. Le choix moderne et plus sûr pour les redirections permanentes sur les API qui acceptent POST/PUT.
Matrice de décision : quel code de redirection ?
| Permanent (cacher pour toujours) | Temporaire (ne pas cacher) | |
|---|---|---|
| La méthode peut passer à GET | 301 Moved Permanently | 302 Found |
| La méthode doit rester identique | 308 Permanent Redirect | 307 Temporary Redirect |
Cas particulier : si vous voulez spécifiquement POST → GET (le pattern Post/Redirect/Get), utilisez 303 See Other.
Pour les pages HTML avec navigation par navigateur, 301 et 302 conviennent en général parce que GET reste GET. Pour les API et les formulaires, préférez 308 et 307 pour éviter les réécritures de méthode surprenantes.
4xx Erreurs client : choisir le bon
4xx signifie que le client a fait quelque chose de mal. Plus votre vocabulaire 4xx est précis, plus votre API est facile à utiliser : les clients peuvent brancher sur le code plutôt que de parser des chaînes d’erreur.
400 Bad Request
Erreur de syntaxe générique. JSON malformé, champ requis manquant au niveau structurel, requête que le serveur n’arrive même pas à parser. Si la requête se parse mais échoue à la validation métier, préférez 422.
401 Unauthorized vs 403 Forbidden
La paire la plus confondue de HTTP. La distinction est simple une fois qu’on l’a vue :
401 Unauthorized: la requête manque d’authentification valide. Le serveur ne sait pas qui vous êtes. Renvoyer les credentials (ou rafraîchir le token) peut résoudre. La réponse doit inclure un en-têteWWW-Authenticateselon la RFC 9110 §15.5.2.403 Forbidden: le serveur sait qui vous êtes et refuse quand même. Réenvoyer la requête n’aidera pas. Il vous faut d’autres credentials ou d’autres permissions.
| Vous voyez | Ce que cela signifie |
|---|---|
401 avec WWW-Authenticate: Bearer | Pas de token, token expiré, ou token invalide |
403 après une connexion réussie | Connecté, mais cet utilisateur ne peut pas accéder à cette ressource |
401 après une connexion réussie | Bug : vous voulez probablement 403 |
Anti-pattern : 403-déguisé-en-404. Certains sites renvoient 403 quand un utilisateur non authentifié demande /admin/dashboard. Cela divulgue l’existence de /admin/dashboard. GitHub résout le problème en renvoyant 404 pour les dépôts privés dont vous n’êtes pas membre : la ressource « n’existe pas » de votre point de vue. C’est un choix délibéré de masquage d’information, pas un bug.
404 Not Found vs 410 Gone
Les deux disent « cette ressource n’est pas ici ». La différence porte sur la permanence et le SEO.
404 Not Found: peut exister, peut ne pas exister, peut revenir. Les moteurs continueront à vérifier.410 Gone: était ici, supprimée délibérément, ne reviendra pas. Les moteurs la retirent de l’index beaucoup plus vite.
Si vous supprimez une page produit et voulez la sortir de l’index Google maintenant, 410 est le bon choix. Si une URL est juste temporairement cassée, 404 convient.
405 Method Not Allowed
L’URL existe mais n’accepte pas cette méthode. La réponse doit inclure un en-tête Allow listant les méthodes supportées.
HTTP/1.1 405 Method Not Allowed
Allow: GET, HEAD, OPTIONS
Content-Type: application/json
{"error": "POST is not allowed on this endpoint"}
Oublier l’en-tête Allow est la violation de contrat n°1 dans les API REST faites maison.
408 Request Timeout
Le client a commencé à envoyer une requête puis s’est tu. Le serveur a abandonné. À ne pas confondre avec 504 Gateway Timeout, qui concerne l’upstream. 408 veut dire « vous, le client, avez mis trop de temps ».
409 Conflict
La requête est en conflit avec l’état actuel. Usage le plus courant : verrouillage optimiste. Le client envoie If-Match: "etag-v3" et l’ETag courant côté serveur est "etag-v4", donc la mise à jour est rejetée avec 409.
410 Gone
Voir plus haut : suppression définitive. Utile pour retirer des enregistrements soft-deleted des index de recherche.
415 Unsupported Media Type
Le client a envoyé un corps que le serveur ne comprend pas. POSTer du XML à une API JSON-only renvoie 415. La réponse devrait suggérer les types acceptables.
422 Unprocessable Content
La requête se parse correctement, mais elle échoue à la validation sémantique. La RFC 9110 a finalement promu ce code de WebDAV vers la spec principale en 2022. Utilisez 422 pour les erreurs de validation :
{
"error": "validation_failed",
"details": [
{"field": "email", "message": "must be a valid email"},
{"field": "age", "message": "must be at least 13"}
]
}
Si votre API hésite entre 400 et 422, la règle d’or : 400 pour « je n’arrive même pas à parser ça », 422 pour « je l’ai parsé et ça n’a pas de sens ».
425 Too Early
Envoyé quand le serveur ne veut pas risquer de traiter une requête susceptible d’être un rejeu de données précoces TLS 1.3. Surtout pertinent pour les CDN et les reverse proxies.
428 Precondition Required
Le serveur exige que vous envoyiez If-Match ou If-Unmodified-Since pour éviter le problème de la mise à jour perdue. Utilisé dans les API d’édition collaborative.
429 Too Many Requests
Rate limited. La réponse doit inclure Retry-After (en secondes ou comme date HTTP) pour que les clients bien élevés puissent reculer.
HTTP/1.1 429 Too Many Requests
Retry-After: 30
Content-Type: application/json
{"error": "rate_limited", "limit": 100, "window": "1m"}
451 Unavailable for Legal Reasons
Le numéro est une référence à Bradbury. Le cas d’usage n’est pas fictif : les retraits DMCA, les suppressions au titre du droit à l’oubli RGPD et les blocages par pays justifient tous 451. La réponse devrait inclure un en-tête Link pointant vers l’autorité légale exigeant le blocage, selon la RFC 7725.
418 I’m a Teapot (l’easter egg)
Oui, il existe vraiment. La RFC 2324 est un poisson d’avril de 1998, et l’IETF l’a maintenu dans les registres parce que trop de produits l’avaient implémenté pour rire. Ne livrez pas 418 dans une vraie API : la plupart des reverse proxies et load balancers le gèrent mal.
Matrice de décision : quel 4xx ?
| Situation | Code |
|---|---|
| Le corps est malformé ou non parseable | 400 |
| Pas d’authentification / token expiré | 401 |
| Authentifié mais non autorisé | 403 |
| L’URL n’existe pas (ou vous la cachez) | 404 |
| L’URL existait, supprimée délibérément | 410 |
| Mauvaise méthode HTTP | 405 (avec Allow) |
Mauvais Content-Type | 415 |
| Conflit de verrou optimiste | 409 |
| Erreur de validation (parse OK, validation KO) | 422 |
| Rate limited | 429 (avec Retry-After) |
| Bloqué pour raisons légales | 451 |
5xx Erreurs serveur : qu’est-ce qui est vraiment cassé
5xx veut dire « notre faute ». Les ingénieurs d’astreinte se soucient surtout de savoir quel 5xx les a réveillés à 3 h du matin, parce que le code indique quelle couche investiguer en premier.
500 Internal Server Error
Le fourre-tout. Cela signifie presque toujours qu’une exception non gérée a remonté jusqu’au handler par défaut du framework. Il ne dit rien de la cause, c’est pour ça que le logging structuré compte plus que le code de statut ici.
501 Not Implemented
Le serveur ne supporte pas du tout la méthode. À ne pas confondre avec 405 (cette méthode n’est pas autorisée pour cette URL). 501 dit « ce serveur n’a aucune idée de ce que PROPFIND veut dire ». Rare en API REST.
502 Bad Gateway
Un reverse proxy ou load balancer a reçu une réponse invalide de l’upstream. L’upstream a répondu, mais avec n’importe quoi : mauvais protocole, en-têtes malformés, connexion coupée en plein milieu de la réponse. Si vous voyez 502 depuis votre CDN, l’origine plante probablement ou renvoie des corps tronqués.
503 Service Unavailable
Le serveur ne sert volontairement pas de requêtes en ce moment. À utiliser pour les fenêtres de maintenance ou les réponses de surcharge gracieuse. Devrait inclure Retry-After.
504 Gateway Timeout
Le reverse proxy a attendu l’upstream et l’upstream n’a jamais répondu à temps. L’upstream est lent ou bloqué, différent de 502, où l’upstream a répondu n’importe quoi.
502 vs 504 : le diagnostic d’astreinte
| Vous voyez | Première chose à vérifier |
|---|---|
502 Bad Gateway | L’upstream répond avec des données invalides : vérifier les logs de l’origine pour des crashs, des réponses malformées, des incompatibilités de protocole |
504 Gateway Timeout | L’upstream est figé : vérifier le CPU de l’origine, les requêtes DB, les appels d’API en aval, et le proxy_read_timeout du proxy |
Une confusion fréquente : une requête de base de données qui prend 60 secondes ressort en 504 si votre proxy timeout à 30 secondes, mais en 500 si le serveur applicatif timeout à 90 secondes et lève une exception. Même cause racine, code différent, ligne de log différente. Entraînez vos dashboards à faire ressortir les deux.
507 Insufficient Storage
Spécifique à WebDAV. Disque plein côté serveur. Si vous voyez ça depuis une API non-WebDAV, quelqu’un détourne le sens du code.
508 Loop Detected
Récursion infinie dans les opérations PROPFIND de WebDAV. Très rare.
511 Network Authentication Required
Codes des captive portals : le WiFi d’un hôtel ou d’un aéroport envoie 511 pour dire à votre navigateur « tu dois d’abord te connecter au portail ». La réponse inclut un Location vers la page du portail.
Matrice de troubleshooting : quelle couche vérifier en premier
| Code | App | Proxy | DB | Réseau |
|---|---|---|---|---|
500 | Oui | — | Peut-être (erreur DB non capturée) | — |
502 | — | Oui (upstream malformé) | — | Peut-être (TCP reset) |
503 | Oui (flag de maintenance) | Oui (rejet rate-limit) | — | — |
504 | Oui (handler lent) | Oui (config de timeout) | Oui (requête lente) | Oui (DNS, perte de paquets) |
Anti-patterns courants des codes HTTP
Ces cinq erreurs représentent l’essentiel du mauvais code que je relis.
1. Emballer les erreurs dans 200 OK
HTTP/1.1 200 OK
{"success": false, "error": "user_not_found"}
Tous les outils de monitoring, les CDN et les caches pensent désormais que la requête a réussi. La logique de retry échoue. Les load balancers conscients du code de statut routent du mauvais trafic vers des backends « sains ». Ce pattern vient de JSON-RPC et a été hérité par GraphQL ; GraphQL le fait parce que les succès partiels demandent un reporting d’erreurs par champ, ce qui est défendable. REST n’a pas d’excuse : utilisez 4xx pour les erreurs client, 5xx pour les erreurs serveur, et mettez le détail structuré dans le corps.
2. Mélanger 401 et 403
Si vos 401 et 403 ne sont pas cohérents, des attaquants peuvent sonder votre API pour découvrir quelles ressources existent. Choisissez une politique : soit renvoyer 404 pour « vous ne pouvez pas voir ça » (l’approche GitHub pour les dépôts privés), soit renvoyer 403 systématiquement. L’incohérence divulgue de l’information.
3. Cacher 403 derrière 404
Parfois correct, souvent un bug. GitHub renvoyant 404 pour les dépôts privés est délibéré, parce que l’existence du dépôt est elle-même sensible. Mais si votre API renvoie 404 pour « ce compte utilisateur est suspendu », des utilisateurs légitimes ne peuvent plus distinguer s’ils ont mal tapé le nom ou s’ils ont été suspendus. Documentez votre politique explicitement et appliquez-la systématiquement.
4. Utiliser 500 comme catch par défaut
Les frameworks rendent ça facile, c’est tout le problème. Chaque exception non capturée devient 500 et votre alerting ne sait plus distinguer « la base de données est down » de « l’utilisateur a passé un UUID malformé ». Capturez les erreurs de validation et levez 400 ou 422. Capturez les NotFound de votre ORM et levez 404. Réservez 500 aux échecs vraiment inattendus, et quand vous le levez, loguez un request ID pour pouvoir corréler.
5. Longues chaînes de redirection
Chaque saut coûte un round trip. Si /old redirige vers /intermediate puis vers /canonical, ce sont deux résolutions DNS et deux handshakes TCP supplémentaires (au pire). Google déclasse spécifiquement la priorité de crawl pour les chaînes de plus de 3 sauts, et les navigateurs plafonnent les chaînes autour de 20 pour éviter les boucles. Aplatissez les chaînes à la source, dans la config de votre CDN ou la table de redirection de votre application.
Codes HTTP et SEO
Les moteurs de recherche traitent les codes de statut comme des signaux faisant autorité pour décider de garder, supprimer ou transférer une URL. Mal les choisir, c’est voir vos classements bouger.
301 vs 302 et le PageRank
301 Moved Permanently transfère le PageRank : Google traite la nouvelle URL comme la destination canonique de tous les signaux pointant vers l’ancienne. 302 Found ne le transfère pas (ou le transfère lentement, selon les heuristiques de Google). Si vous avez renommé une URL pour de bon, utilisez 301. Si vous redirigez un visiteur vers /login, utilisez 302.
404 vs 410 vs Soft 404
Google distingue trois états « manquants » :
404 Not Found: Google revérifie périodiquement et garde l’URL dans l’index pendant un certain temps.410 Gone: Google retire l’URL plus vite, souvent en un seul cycle de crawl.- Soft 404 : terme de Google pour une page qui renvoie
200 OKmais affiche un message « not found ». Google le détecte à partir de motifs de contenu et la traite comme un404de toute façon, mais vous avez gaspillé une requête de crawl et possiblement dilué votre vrai contenu.
Si vous nettoyez un index qui traîne, renvoyez de vrais 410 pour les URL définitivement supprimées.
5xx et budget de crawl
Le crawler de Google réduit son rythme quand un site renvoie des 5xx persistants. Le rapport Crawl Stats de Search Console le montre : un pic soutenu d’erreurs 5xx peut faire chuter votre budget de crawl pendant des jours, ce qui veut dire que les nouvelles pages mettent plus de temps à être indexées. Traitez les taux de 5xx comme une métrique SEO, pas seulement comme une métrique de fiabilité.
Le 200 OK qui est en fait cassé
Renvoyer 200 OK avec une page d’erreur (l’anti-pattern soft-404) est le pire cas pour le SEO. Google indexe le message d’erreur, le classe pour rien, et finit lentement par comprendre que la page est cassée. Renvoyez toujours le bon code de statut depuis le serveur, même si votre SPA affiche une UI d’erreur soignée.
Comment inspecter les codes HTTP (outillage)
On ne peut pas réparer ce qu’on ne voit pas. Tout développeur en activité devrait être à l’aise avec au moins trois de ces outils.
Onglet Network des DevTools
Chrome, Firefox et Safari affichent tous une colonne Status dans l’onglet Network. Clic droit sur l’en-tête de colonne pour ajouter Status Text s’il n’est pas visible. Astuces utiles :
- Preserve log : garder les entrées entre les navigations pour voir toute la chaîne de redirection.
- Filtrer par statut : tapez
status-code:5xx(Chrome) pour ne voir que les erreurs serveur. - Replay XHR : clic droit sur n’importe quelle requête puis Replay XHR pour la rejouer sans recharger la page.
Pour les redirections, dépliez la requête pour voir chaque saut et le code de statut à chaque étape.
curl (la réponse universelle)
curl montre tout. Trois patterns qui résolvent 90 % du débogage :
# Just the status code
curl -o /dev/null -s -w "%{http_code}\n" https://api.example.com/users/1
# Headers only (HEAD request, follow redirects)
curl -I -L https://example.com
# Full verbose with request and response headers
curl -v https://api.example.com/users/1
Quand vous construisez des URL de test avec des caractères spéciaux dans les query strings, utilisez --data-urlencode pour laisser curl gérer l’encodage, ou collez l’URL dans notre Décodeur et Encodeur URL pour vérifier quels octets vont vraiment passer sur le câble.
# curl encodes the query value for you
curl -G "https://api.example.com/search" \
--data-urlencode "q=hello world & friends"
# Sends: GET /search?q=hello%20world%20%26%20friends
JavaScript fetch
La propriété Response.status contient le code entier. Response.ok vaut true pour tout 2xx.
const res = await fetch('https://api.example.com/users/1');
console.log(res.status); // 200
console.log(res.statusText); // "OK"
console.log(res.ok); // true
if (!res.ok) {
if (res.status === 401) {
// refresh token and retry
} else if (res.status === 429) {
const retryAfter = Number(res.headers.get('Retry-After')) || 1;
await new Promise(r => setTimeout(r, retryAfter * 1000));
} else if (res.status >= 500) {
throw new Error(`Server error: ${res.status}`);
}
}
En axios, la même logique vit dans les intercepteurs :
import axios from 'axios';
axios.interceptors.response.use(
response => response,
error => {
const status = error.response?.status;
if (status === 401) {
// redirect to login
}
return Promise.reject(error);
}
);
Python requests
import requests
r = requests.get('https://api.example.com/users/1')
print(r.status_code) # 200
print(r.reason) # 'OK'
# Raises requests.exceptions.HTTPError for 4xx/5xx
r.raise_for_status()
# Manual handling
if r.status_code == 429:
retry_after = int(r.headers.get('Retry-After', '1'))
time.sleep(retry_after)
elif 500 <= r.status_code < 600:
raise RuntimeError(f'Server error: {r.status_code}')
raise_for_status() est l’idiome Python pour « échoue bruyamment sur 4xx ou 5xx ». À utiliser dans les scripts où vous voulez des exceptions sur erreur plutôt que de brancher sur status_code.
Postman et Bruno
Les deux permettent d’asserter sur les codes de statut dans un script de test :
// Postman/Bruno test script
pm.test("Status is 201", () => {
pm.response.to.have.status(201);
});
pm.test("Has Location header", () => {
pm.expect(pm.response.headers.get('Location')).to.match(/^\/users\/\d+$/);
});
Lancez-les contre la staging en CI pour attraper les violations de contrat avant la production.
FAQ
Quelle est la différence entre 401 et 403 ?
401 Unauthorized veut dire que le serveur ne sait pas qui vous êtes : vos credentials sont absents, expirés ou invalides. 403 Forbidden veut dire que le serveur sait qui vous êtes et refuse quand même. Si envoyer d’autres credentials peut résoudre, utilisez 401. Sinon, utilisez 403.
Quand utiliser 301 ou 302 ?
Utilisez 301 quand le déplacement est permanent : l’ancienne URL ne reviendra jamais et vous voulez que les moteurs transfèrent le PageRank à la nouvelle. Utilisez 302 pour les redirections temporaires où l’URL d’origine reste canonique (flux de connexion, A/B testing, pages de maintenance). Pour les API, préférez 308 et 307 parce qu’ils préservent la méthode de la requête.
Que signifie une erreur 502 Bad Gateway ?
502 veut dire qu’un reverse proxy ou load balancer a reçu une réponse invalide du serveur upstream. L’upstream a répondu, mais avec n’importe quoi : mauvais protocole, en-têtes malformés ou connexion coupée. C’est différent de 504 Gateway Timeout, où l’upstream n’a pas répondu du tout. Premier endroit à vérifier : les logs du serveur d’origine pour des crashs ou des réponses tronquées.
Qu’est-ce qu’un « soft 404 » ?
Un « soft 404 » est une page qui renvoie 200 OK mais affiche en fait un message « not found ». Google les détecte par heuristique et les traite comme des 404 de toute façon. Ils gaspillent du budget de crawl et peuvent diluer votre vrai contenu. Renvoyez toujours de vrais codes 404 ou 410 depuis le serveur, même si votre SPA affiche une UI d’erreur soignée.
Quand utiliser 422 plutôt que 400 ?
Utilisez 400 Bad Request quand le serveur n’arrive même pas à parser la requête : JSON malformé, champs structurels manquants, erreurs de syntaxe. Utilisez 422 Unprocessable Content quand la requête se parse correctement mais échoue à la validation métier : format d’email invalide, valeur hors plage, champs sémantiquement incohérents. Le raccourci : 400 pour la syntaxe, 422 pour la sémantique.
Comment répondre à 429 Too Many Requests ?
Lisez l’en-tête Retry-After (un nombre de secondes ou une date HTTP) et reculez d’au moins cette durée avant de retenter. Si Retry-After est absent, utilisez un backoff exponentiel avec jitter en partant d’environ 1 seconde. Ne retentez jamais immédiatement, c’est comme ça qu’on se fait bannir.
Les codes informationnels 1xx sont-ils encore utilisés en 2026 ?
Oui, mais la plupart sont invisibles depuis le code applicatif. 100 Continue et 101 Switching Protocols sont des fonctionnalités de base d’HTTP/1.1. 103 Early Hints est de plus en plus utilisé par Cloudflare, Fastly et Vercel pour pousser des indices de preload avant la réponse principale ; il améliore sensiblement le Largest Contentful Paint. La plupart des bibliothèques HTTP replient les 1xx dans la réponse finale, donc on ne les voit typiquement que dans les DevTools ou avec curl -v.
418 « I’m a teapot » est-il un vrai code de statut ?
Oui, étonnamment. La RFC 2324 était un poisson d’avril de 1998, mais assez de produits l’ont implémenté pour que l’IETF le maintienne dans les registres via la RFC 7168. Ne livrez pas 418 en production : beaucoup de reverse proxies et load balancers ne le gèrent pas correctement, et il n’a aucune utilité réelle hors de la blague.