El fallo se ve así. Una integración envía un POST a tu endpoint. El endpoint funciona, la ruta existe, todo se comprueba a mano sin problemas. Pero en los registros aparece un GET contra una ruta que solo acepta POST, respondido con 404 o 405.

La causa casi siempre es la misma: entre el cliente y el endpoint hay una redirección 301 o 302. Y esos códigos no obligan al cliente a conservar el método.

Puedes comprobarlo en diez segundos:

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

Salida: método tras la redirección: GET. El cuerpo de la petición se pierde con él.

Cambia 301 por 308 en la misma orden y obtendrás POST. La única diferencia es el código de estado.

Cinco códigos, dos propiedades

Las redirecciones se distinguen por dos ejes independientes: si son permanentes o temporales, y si sobrevive el método de la petición.

Código Nombre Permanencia Conserva el método
301 Moved Permanently permanente no
302 Found temporal no
303 See Other no, siempre GET
307 Temporary Redirect temporal
308 Permanent Redirect permanente

Fíjate en la simetría: 307 es un 302 con garantía de método, y 308 es un 301 con garantía de método.

De dónde viene la confusión

Formalmente, la especificación nunca exigió cambiar el método en 301 y 302. Pero los primeros navegadores y clientes hacían exactamente eso, y cuando se detectó el desajuste toda la web ya funcionaba así.

De modo que el comportamiento se codificó como permitido: RFC 9110, la especificación vigente de la semántica HTTP, permite al cliente cambiar POST por GET en 301 y 302. Lo permite, pero no lo exige, lo que deja el comportamiento al criterio del cliente y hace imposible confiar en él.

Para ofrecer una opción inequívoca se introdujeron 307 y 308. En ellos cambiar el método está prohibido: el cliente debe repetir la petición con el mismo método y el mismo cuerpo. El código 308 está definido en su propio RFC 7538, con una descripción de referencia en la documentación de MDN.

Conclusión práctica: 301 y 302 solo son seguros allí donde la petición es con certeza un GET. Donde pueda llegar un POST hacen falta 307 o 308.

La segunda trampa: las redirecciones permanentes se cachean

Esta se olvida más a menudo que el problema del método.

301 y 308 declaran el traslado permanente, y un navegador tiene derecho a cachearlas durante mucho tiempo, a veces hasta que el usuario borre la caché. Después de eso, sencillamente no se pregunta al servidor.

De ahí la regla: un 301 equivocado es casi imposible de revertir. Corregirás la configuración, pero los visitantes que ya recibieron la respuesta antigua seguirán siendo redirigidos. Con 302 y 307 no ocurre: son temporales por definición.

Consejo práctico: mientras el esquema de traslados no esté asentado, usa códigos temporales. Ascender un 302 a 301 después es fácil; el camino inverso, no.

Qué elegir

La página se ha mudado definitivamente y solo acepta GET → 301. El caso clásico: la URL antigua de un artículo, un cambio de estructura de direcciones.

Lo mismo, pero el endpoint acepta POST → 308. Aquí entran API, webhooks y formularios.

El destino cambia temporalmente, solo GET → 302.

Temporal, y el método debe conservarse → 307.

Tras un POST, enviar al usuario a una página de resultado → 303. Es el patrón POST/Redirect/GET: convierte deliberadamente la petición en GET para que recargar la página no reenvíe el formulario. Aquí el cambio de método es el objetivo, no un fallo.

Dónde se rompe en la práctica

Redirección http→https delante de una API. El caso más frecuente y más molesto. El cliente está configurado con http://, el servidor responde 301 apuntando a https://, el método se pierde. Síntoma: GET en los registros sobre una ruta POST. Se arregla sustituyendo 301 por 308.

Canonicalización de host. La redirección de www a sin www (o al revés) suele montarse como 301. Está bien para páginas, pero si en ese mismo host vive una API, aplica exactamente el mismo problema.

Envío de formularios. Un formulario se envía a una dirección que redirige. El usuario ve que «no pasó nada» porque los datos nunca llegaron al manejador.

Webhooks. Un servicio externo envía un POST a una dirección que le diste una vez. Si esa dirección se mudó tras un 301, los webhooks dejan de funcionar en silencio mientras el remitente registra una respuesta correcta a la redirección.

Los cuatro comparten una firma: el endpoint está sano pero la petición no le llega en su forma original. Por eso el diagnóstico se desvía: se busca el error en el manejador y está en la configuración del servidor web.

Por qué los acortadores usan 302

Un enlace corto casi siempre devuelve 302, y es una decisión deliberada, no un descuido.

El motivo es la permanencia, no el método: el destino de un enlace corto puede cambiar. Usar 301 permitiría a los navegadores cachear el salto para siempre, y tras cambiar el destino parte de los visitantes seguiría aterrizando en la dirección antigua. El método aquí es irrelevante: los enlaces cortos se siguen con peticiones GET.

El mecanismo está tratado aparte en cómo funcionan los acortadores de enlaces.

Qué no cambia la elección del código

Dos ideas equivocadas muy extendidas.

La autoridad del enlace. Elegir entre 301 y 302 no afecta a cómo se transmite la autoridad: Google lo confirmó ya en 2016. El tratamiento completo está en ¿transmiten autoridad los enlaces acortados?.

La selección de la URL canónica. Una redirección y rel="canonical" resuelven problemas distintos: la redirección retira una dirección de circulación, el canonical deja ambas accesibles y señala la principal. Cuándo usar cada uno está descrito en la guía de canonical.

Lista de comprobación

  • En rutas que aceptan POST, usar 307 o 308, no 302 ni 301.
  • Revisar la redirección http→https por separado: suele ponerse con una regla general y la API se olvida.
  • Mientras el esquema no esté asentado, códigos temporales. 301 y 308 se cachean y apenas son reversibles.
  • Tras procesar un formulario, usar 303: es el comportamiento correcto, no un error.
  • Reducir las cadenas a un solo salto: cada salto extra añade latencia y riesgo.