Chapitre 316 minIntermédiaire
Codes de statut HTTP : 200, 201, 400, 401, 403, 404, 500
Par Josselyn C., prof de BTS SIO. Mis à jour le .
À la fin de ce chapitre, tu sauras
- classer un code de statut dans sa famille et dire qui doit agir ;
- choisir entre 401, 403 et 404, et entre 400, 409 et 422 ;
- repérer les mauvais choix de code dans une API existante ;
- lire un journal d'accès et y reconnaître une anomalie.
3 exercices corrigés1 schéma
Un code de statut HTTP est le nombre à trois chiffres que le serveur place en tête de chaque réponse pour dire ce qui s'est passé : 200 pour une réussite, 404 pour une ressource introuvable, 500 pour une panne du serveur. Le premier chiffre donne la famille (succès, redirection, erreur du client, erreur du serveur). Ce chapitre présente les codes qu'une API utilise vraiment, comment choisir entre des codes voisins (401, 403 et 404 ; 400 et 422) et les erreurs de choix les plus fréquentes.
Cinq familles
| Famille | Sens | Qui doit agir | Exemples |
|---|---|---|---|
1xx Information | Réponse provisoire, la suite arrive | Personne : c'est géré par le protocole | 100 Continue, 103 Early Hints |
2xx Succès | La requête a été comprise et traitée | Personne | 200, 201, 202, 204 |
3xx Redirection | La ressource est ailleurs, ou la copie du client est encore bonne | Le client suit la redirection ou réutilise sa copie | 301, 304, 307, 308 |
4xx Erreur du client | La requête est incorrecte ou refusée | Le client doit corriger sa requête | 400, 401, 403, 404, 409, 422, 429 |
5xx Erreur du serveur | La requête semblait correcte, mais le serveur a échoué | Le serveur (ses développeurs, son exploitant) | 500, 502, 503, 504 |
La distinction entre 4xx et 5xx est la plus importante : elle dit de quel côté est le problème. Un client qui reçoit 400 sait que renvoyer la même requête ne servira à rien ; un client qui reçoit 503 peut réessayer plus tard.
Les codes de succès (2xx)
| Code | Libellé | Quand le renvoyer | Corps |
|---|---|---|---|
200 | OK | Lecture réussie (GET), modification réussie (PUT, PATCH) qui renvoie la ressource | La ressource ou la liste |
201 | Created | Une ressource a été créée, en général par POST | La ressource créée, avec l'en-tête Location qui donne son URI |
202 | Accepted | La demande est acceptée mais sera traitée plus tard (export, génération de PDF, envoi de masse) | Souvent l'adresse où suivre l'avancement |
204 | No Content | Réussite sans rien à renvoyer : suppression, modification qui ne renvoie pas la ressource | Aucun corps |
Une liste vide est une réussite : GET /v1/fiches?etat=VA qui ne trouve aucune fiche validée renvoie 200 avec une liste vide ([]), pas 404. La collection existe ; elle ne contient simplement rien qui corresponde au filtre.
Les redirections (3xx)
Une API redirige rarement, mais deux cas arrivent :
304 Not Modifiedrépond à une requête conditionnelle (If-None-Match) : la copie du client est toujours à jour (chapitre 2) ;308 Permanent Redirectet307 Temporary Redirectdéplacent une ressource en conservant la méthode et le corps. Leurs ancêtres301et302autorisent historiquement le client à transformer unPOSTenGETlors de la redirection, ce qui fait perdre le corps. Pour une API, préfère308et307.
Les erreurs du client (4xx)
| Code | Libellé | Quand le renvoyer | Exemple dans AppliFrais |
|---|---|---|---|
400 | Bad Request | La requête est mal formée : JSON illisible, paramètre de type incorrect | Corps {"mois": 2026-11 (accolade manquante) |
401 | Unauthorized | Pas d'authentification valide : jeton absent, invalide ou expiré. Ajoute l'en-tête WWW-Authenticate | Requête sans en-tête Authorization |
403 | Forbidden | Le client est authentifié mais n'a pas le droit | Un visiteur tente de valider une fiche, action réservée aux comptables |
404 | Not Found | La ressource n'existe pas (ou le client ne doit pas savoir qu'elle existe) | GET /v1/fiches/999 |
405 | Method Not Allowed | La méthode n'est pas prévue sur cette URI. Ajoute l'en-tête Allow | PUT /v1/fiches (on ne remplace pas une collection) |
406 | Not Acceptable | Aucun format demandé dans Accept n'est disponible | Accept: application/xml sur une API JSON |
409 | Conflict | La requête contredit l'état actuel de la ressource | Créer une deuxième fiche pour le même mois ; clôturer une fiche déjà validée |
410 | Gone | La ressource a existé et a été retirée définitivement | Une ancienne version de l'API arrêtée |
412 | Precondition Failed | La condition If-Match n'est plus vraie : quelqu'un a modifié la ressource entre-temps | Deux comptables valident la même fiche en même temps |
413 | Content Too Large | Le corps dépasse la taille permise | Un justificatif de 40 Mo pour une limite de 5 Mo |
415 | Unsupported Media Type | Le format du corps (Content-Type) n'est pas accepté | Corps envoyé en XML à une API JSON |
422 | Unprocessable Content | Le JSON est bien formé, mais son contenu viole les règles de validation | {"mois":"2026-13"} : il n'y a pas de 13e mois |
428 | Precondition Required | Le serveur exige une requête conditionnelle (If-Match) pour éviter les écrasements | Modification d'une fiche sans If-Match |
429 | Too Many Requests | Le client a dépassé la limite de débit. Ajoute Retry-After | Plus de 100 requêtes par minute avec la même clé |
Les erreurs du serveur (5xx)
| Code | Libellé | Ce qui s'est passé |
|---|---|---|
500 | Internal Server Error | Une erreur non prévue dans le code : exception non attrapée, bogue. Le détail va dans le journal, jamais dans la réponse |
501 | Not Implemented | Le serveur ne connaît pas la méthode demandée |
502 | Bad Gateway | Un intermédiaire (reverse proxy nginx, passerelle d'API) a reçu une réponse invalide du serveur d'application, souvent parce qu'il est arrêté ou a planté |
503 | Service Unavailable | Le service est momentanément indisponible : maintenance, surcharge. Ajoute Retry-After si tu connais la durée |
504 | Gateway Timeout | L'intermédiaire n'a pas reçu de réponse à temps du serveur d'application ou d'un service appelé |
401, 403 ou 404 ?
C'est la question d'examen la plus fréquente sur les API. Pose-toi les questions dans l'ordre :
- Le serveur sait-il qui fait la requête ? Pas de jeton, jeton expiré, signature fausse, clé d'API inconnue :
401 Unauthorized. Le nom du code est trompeur, il signifie en réalité « non authentifié ». - Le client a-t-il le droit de faire cette action sur cette ressource ? Le visiteur est bien identifié mais veut valider une fiche (réservé aux comptables) :
403 Forbidden. - La ressource existe-t-elle ?
GET /v1/fiches/999alors que la fiche 999 n'existe pas :404 Not Found.
Un cas mérite réflexion : Lucas demande la fiche 1188, qui existe mais appartient à une autre visiteuse. Répondre 403 est exact, mais révèle que la fiche 1188 existe : en essayant 1187, 1189, 1190…, un curieux dresse la liste des fiches. Beaucoup d'API répondent donc 404 pour une ressource qui appartient à quelqu'un d'autre : pour ce client, elle n'existe pas. Les deux réponses se défendent ; l'essentiel est que la fiche ne soit jamais renvoyée. Ce contrôle manquant est la faille n° 1 des API (BOLA, chapitre 10).
400 ou 422 ?
Les deux signalent une requête à corriger. La frontière :
400 Bad Request: la requête est mal formée. Le serveur ne peut même pas la lire : JSON syntaxiquement faux, paramètre?page=abcalors qu'un nombre est attendu, en-tête obligatoire absent.422 Unprocessable Content: la requête est bien formée, le JSON est valide et du bon type, mais son contenu viole une règle de validation : un mois2026-13, un montant négatif, un champ obligatoire vide.
Toutes les API ne font pas cette distinction : beaucoup renvoient 400 pour tout problème de saisie, et ce n'est pas faux. Ce qui compte, c'est la cohérence dans toute l'API, et un corps d'erreur qui dit précisément quel champ pose problème.
Et 409 ? Il ne concerne pas le format mais l'état : la fiche d'octobre est valide en soi, mais elle existe déjà ; la demande de clôture est bien formée, mais la fiche est déjà validée. Les données seraient acceptables dans un autre contexte.
L'ordre de l'arbre suit l'ordre des contrôles dans le code : on authentifie d'abord (sinon on ne sait pas quoi vérifier), on lit la requête, on cherche la ressource, on vérifie les droits, on valide les données, puis on applique les règles de gestion. Quand la ressource appartient à un autre client, l'API peut fusionner les questions 3 et 4 et répondre 404, comme expliqué plus haut.
Les libellés ne comptent pas
Le libellé qui suit le code (OK, Not Found) est un texte destiné aux humains ; les programmes l'ignorent, et HTTP/2 ne le transmet même plus. Il a d'ailleurs changé pour deux codes avec la RFC 9110 : 413 s'appelle désormais Content Too Large (anciennement Payload Too Large) et 422 Unprocessable Content (anciennement Unprocessable Entity). Beaucoup de serveurs affichent encore les anciens libellés : Node.js répond HTTP/1.1 422 Unprocessable Entity. C'est sans conséquence.
Les erreurs de choix les plus fréquentes
| Erreur | Pourquoi c'est faux | Correction |
|---|---|---|
200 avec {"erreur": "Fiche introuvable"} | Le client, le cache, la supervision et le navigateur croient à une réussite ; le front doit lire le corps pour comprendre | 404 avec un corps d'erreur |
500 quand une saisie est invalide | Le problème vient du client ; un 500 fait croire à une panne et déclenche des alertes | 400 ou 422 |
404 pour une liste vide | La collection existe ; elle est vide | 200 et [] |
401 quand le rôle est insuffisant | Le client est bien authentifié ; se reconnecter ne changera rien | 403 |
403 sans jeton | Le serveur ne sait pas qui appelle | 401 avec WWW-Authenticate |
200 après une création | Le client ne sait pas qu'une ressource a été créée, ni où | 201 avec Location |
Le détail d'une exception renvoyé avec un 500 | Fuite d'informations : chemins de fichiers, requêtes SQL, versions | Message générique au client, détail dans le journal |
Exercices
Exercice 1 sur 3 Le bon code de statut Réussi À revoir
Donne le code que l'API d'AppliFrais doit renvoyer dans chaque situation.
- Lucas lit la liste de ses fiches.
- Lucas crée sa fiche de décembre 2026.
- Lucas supprime un frais hors forfait de sa fiche en cours ; l'API ne renvoie rien.
- Une requête arrive sans en-tête
Authorization. - Lucas, visiteur, appelle
PATCH /v1/fiches/12avec{"etat":"VA"}: la validation est réservée aux comptables. - Le corps envoyé est
{"montant": 23.5,(le JSON s'arrête là). - Le corps est un JSON valide, mais le montant vaut
-15. - Lucas crée une fiche pour octobre 2026, qui existe déjà.
- Un script envoie 300 requêtes en une minute, pour une limite de 100.
- La base de données ne répond plus et l'API ne peut rien lire.
- Le front demande
GET /v1/fiches?etat=MPet Lucas n'a aucune fiche mise en paiement. - Un client envoie
DELETE /v1/fiches, qui n'est pas prévu.
Voir la réponseMasquer la réponse de l'exercice 1
200 OK, avec la liste.201 Created, avec la fiche dans le corps etLocation: /v1/fiches/<id>.204 No Content.401 Unauthorized, avecWWW-Authenticate: Bearer.403 Forbidden: Lucas est authentifié mais son rôle ne permet pas de valider.400 Bad Request: le JSON est mal formé, le serveur ne peut pas le lire.422 Unprocessable Content(ou400si l'API ne distingue pas les deux) : le JSON est valide mais la donnée viole une règle.409 Conflict: la demande contredit l'état existant (une seule fiche par mois).429 Too Many Requests, avecRetry-After.503 Service Unavailablesi l'API détecte l'indisponibilité et la signale ; un500si l'erreur n'est pas gérée. Dans les deux cas, aucun détail technique dans la réponse.200 OKavec une liste vide : la collection existe.405 Method Not Allowed, avecAllow: GET, POST.
Tu avais trouvé ?
Exercice 2 sur 3 Corriger les codes d'une API Réussi À revoir
Un stagiaire a écrit une API de prêt de matériel. Repère et corrige les codes inadaptés.
| Requête | Situation | Code renvoyé |
|---|---|---|
POST /v1/prets | Prêt créé | 200 |
GET /v1/prets/45 | Le prêt 45 n'existe pas | 200 avec {"erreur":"inconnu"} |
POST /v1/prets | Date de retour antérieure à la date de prêt | 500 |
DELETE /v1/prets/12 | Jeton valide, mais l'utilisateur n'est pas gestionnaire | 401 |
GET /v1/materiels?type=tablette | Aucune tablette | 404 |
POST /v1/prets | Le matériel demandé est déjà prêté | 400 |
Voir la réponseMasquer la réponse de l'exercice 2
| Requête | Code correct | Raison |
|---|---|---|
POST /v1/prets, prêt créé | 201 avec Location | Une ressource a été créée |
GET /v1/prets/45, inexistant | 404 | Le code doit dire l'échec, pas le corps seul |
| Dates incohérentes | 422 (ou 400) | Erreur de saisie du client, pas panne du serveur |
| Pas gestionnaire | 403 | L'utilisateur est authentifié ; c'est son rôle qui est insuffisant |
| Aucune tablette | 200 et [] | Une liste vide est une réussite |
| Matériel déjà prêté | 409 | Le conflit vient de l'état actuel du matériel, pas du format de la requête |
Tu avais trouvé ?
Exercice 3 sur 3 Lire un journal d'accès Réussi À revoir
Voici un extrait du journal du reverse proxy devant l'API (format simplifié : heure, adresse IP, requête, code).
09:14:02 203.0.113.50 GET /v1/fiches/1180 200
09:14:02 203.0.113.50 GET /v1/fiches/1181 404
09:14:03 203.0.113.50 GET /v1/fiches/1182 200
09:14:03 203.0.113.50 GET /v1/fiches/1183 200
09:14:03 203.0.113.50 GET /v1/fiches/1184 200
09:14:04 203.0.113.50 GET /v1/fiches/1185 429
09:31:47 198.51.100.7 POST /v1/fiches 502
- Que fait le client 203.0.113.50 ? Sachant qu'un visiteur ne possède qu'une fiche par mois, quel problème ces codes
200révèlent-ils ? - Quel mécanisme l'a arrêté à 09:14:04 ?
- Que t'apprend la dernière ligne, et où chercher la cause ?
Voir la réponseMasquer la réponse de l'exercice 3
- Il énumère les fiches en incrémentant l'identifiant, quatre requêtes par seconde. Un visiteur ne peut pas posséder les fiches 1180, 1182, 1183 et 1184 : l'API renvoie donc les fiches d'autres visiteurs. Elle vérifie l'authentification mais pas que la fiche appartient au demandeur : c'est une faille de contrôle d'accès à l'objet (BOLA). Elle aurait dû répondre
404(ou403) pour chaque fiche d'autrui. - La limitation de débit :
429 Too Many Requests. Elle freine l'énumération mais ne corrige pas la faille. 502 Bad Gateway: le reverse proxy n'a pas obtenu de réponse valide du serveur d'application. Il faut regarder si le processus de l'API tourne encore et lire son journal à 09:31:47, pas chercher une erreur dans la requête du client.
Tu avais trouvé ?