Le bug se présente ainsi. Une intégration envoie un POST vers votre point d'entrée. Le point d'entrée fonctionne, la route existe, tout se vérifie à la main. Mais les journaux montrent un GET sur une route qui n'accepte que POST, avec une réponse 404 ou 405.

La cause est presque toujours la même : une redirection 301 ou 302 se trouve entre le client et le point d'entrée. Et ces codes n'obligent pas le client à conserver la méthode.

La vérification prend dix secondes :

curl -sS -o /dev/null -L -d 'test=1' \
  -w 'méthode après la redirection : %{method}\n' \
  'https://httpbingo.org/redirect-to?url=/anything&status_code=301'

Sortie : méthode après la redirection : GET. Le corps de la requête est perdu avec elle.

Remplacez 301 par 308 dans la même commande et vous obtenez POST. La seule différence est le code de statut.

Cinq codes, deux propriétés

Les redirections se distinguent selon deux axes indépendants : permanente ou temporaire, et conservation ou non de la méthode.

Code Nom Permanence Méthode conservée
301 Moved Permanently permanente non
302 Found temporaire non
303 See Other non, toujours GET
307 Temporary Redirect temporaire oui
308 Permanent Redirect permanente oui

Notez la symétrie : 307 est un 302 avec garantie de méthode, et 308 un 301 avec garantie de méthode.

D'où vient cette confusion

Formellement, la spécification n'a jamais exigé de changer la méthode sur 301 et 302. Mais les premiers navigateurs et clients faisaient exactement cela, et lorsque l'écart a été remarqué, tout le web fonctionnait déjà ainsi.

Le comportement a donc été codifié comme autorisé : la RFC 9110, spécification en vigueur de la sémantique HTTP, permet au client de remplacer POST par GET sur 301 et 302. Elle le permet mais ne l'exige pas — le comportement reste donc à la discrétion du client et ne peut servir de garantie.

Pour offrir une option sans ambiguïté, 307 et 308 ont été introduits. Pour eux, changer la méthode est interdit : le client doit rejouer la requête avec la même méthode et le même corps. Le code 308 est défini dans sa propre RFC 7538, avec une description de référence dans la documentation MDN.

Conclusion pratique : 301 et 302 ne sont sûrs que là où la requête est certainement un GET. Partout où un POST peut arriver, il faut 307 ou 308.

Le second piège : les redirections permanentes sont mises en cache

Celui-ci est oublié plus souvent encore que le problème de méthode.

301 et 308 déclarent le déplacement permanent, et un navigateur est en droit de les conserver en cache longtemps — parfois jusqu'à ce que l'utilisateur vide son cache. Le serveur n'est alors tout simplement plus interrogé.

D'où la règle : un 301 posé par erreur est presque impossible à annuler. Vous corrigerez la configuration, mais les visiteurs ayant déjà reçu l'ancienne réponse continueront d'être redirigés. Avec 302 et 307 le problème ne se pose pas : ils sont temporaires par définition.

Conseil pratique : tant qu'un schéma de migration n'est pas stabilisé, utilisez les codes temporaires. Faire passer un 302 en 301 plus tard est simple ; l'inverse ne l'est pas.

Que choisir

La page a déménagé définitivement et n'accepte que GET → 301. Cas classique : ancienne URL d'article, changement de structure d'adresses.

Idem, mais le point d'entrée accepte POST → 308. Cela couvre les API, les webhooks et les formulaires.

La destination change temporairement, GET uniquement → 302.

Temporaire, et la méthode doit survivre → 307.

Après un POST, envoyer l'utilisateur vers une page de résultat → 303. C'est le motif POST/Redirect/GET : il transforme délibérément la requête en GET pour qu'un rechargement ne renvoie pas le formulaire. Ici le changement de méthode est le but, non un défaut.

Où cela casse en pratique

Une redirection http→https devant une API. Le cas le plus fréquent et le plus pénible. Le client est configuré en http://, le serveur répond 301 vers https://, la méthode est perdue. Symptôme : un GET dans les journaux sur une route POST. Se corrige en remplaçant 301 par 308.

Canonisation de l'hôte. La redirection www vers sans www (ou l'inverse) est généralement montée en 301. Très bien pour des pages, mais si une API vit sur le même hôte, exactement le même problème s'applique.

Envoi de formulaires. Un formulaire est envoyé vers une adresse qui redirige. L'utilisateur constate qu'« il ne s'est rien passé », car les données n'ont jamais atteint le gestionnaire.

Webhooks. Un service externe envoie un POST à une adresse que vous lui avez donnée un jour. Si cette adresse a déménagé derrière un 301, les webhooks cessent silencieusement de fonctionner, tandis que les journaux de l'expéditeur affichent une réponse réussie à la redirection.

Les quatre partagent une signature : le point d'entrée est sain, mais la requête ne lui parvient pas sous sa forme d'origine. Le diagnostic part donc dans la mauvaise direction — on cherche le bug dans le gestionnaire alors qu'il est dans la configuration du serveur web.

Pourquoi les raccourcisseurs utilisent 302

Un lien court renvoie presque toujours un 302, et c'est un choix délibéré, pas une négligence.

La raison tient à la permanence, pas à la méthode : la destination d'un lien court peut changer. Utiliser 301 laisserait les navigateurs mettre le saut en cache pour toujours, et après un changement de destination une partie des visiteurs continuerait d'atterrir à l'ancienne adresse. La méthode est ici sans objet : les liens courts sont suivis par des requêtes GET.

Le mécanisme lui-même est traité à part dans comment fonctionnent les raccourcisseurs de liens.

Ce que le choix du code ne change pas

Deux idées fausses très répandues.

Le jus de lien. Choisir entre 301 et 302 n'influe pas sur la transmission du jus — Google l'a confirmé dès 2016. Le traitement complet se trouve dans les liens raccourcis transmettent-ils le jus de lien ?.

Le choix de l'URL canonique. Une redirection et rel="canonical" résolvent des problèmes différents : la redirection retire une adresse de la circulation, le canonical laisse les deux accessibles et désigne la principale. Quand utiliser l'un ou l'autre est décrit dans le guide du canonical.

Aide-mémoire

  • Sur les routes qui acceptent POST, utiliser 307 ou 308, pas 302 ni 301.
  • Vérifier la redirection http→https séparément : elle est souvent posée par une règle globale et l'API est oubliée.
  • Tant que le schéma n'est pas stabilisé, codes temporaires. 301 et 308 sont mis en cache et à peine réversibles.
  • Après traitement d'un formulaire, utiliser 303 : c'est le comportement correct, pas une erreur.
  • Réduire les chaînes à un seul saut : chaque saut supplémentaire ajoute de la latence et du risque.