Códigos de error HTTP más comunes: 400, 401, 403, 404, 422, 429, 500, 502 y 503

Una docena de códigos de estado explica casi todos los fallos que vas a depurar. Lo difícil no es la lista, sino las parejas que parecen intercambiables y no lo son: 401 frente a 403, 400 frente a 422, 502 frente a 503. Cada una apunta a una capa distinta, así que acertar te dice dónde buscar antes de abrir un solo log.

Lee primero el primer dígito

El primer dígito indica quién es responsable, y eso acota la búsqueda más que el propio código. 4xx significa que la petición era incorrecta: algo en el método, la ruta, las cabeceras, la autenticación o el cuerpo no satisfizo al servidor, y repetirla sin cambios volverá a fallar. 5xx significa que la petición era aceptable pero el servidor, o algo detrás de él, falló, así que la misma petición puede funcionar al reintentarla.

Esa distinción decide dónde miras. Un 4xx te lleva al cliente: la URL, el token, el payload. Un 5xx te lleva al servidor, sus dependencias y sus logs. Perseguir un 502 por el código que construye la petición es tiempo perdido.

Dos clases se olvidan con facilidad. 2xx incluye más que 200: 201 Created devuelve el recurso nuevo y 204 No Content tiene el cuerpo vacío a propósito, así que intentar parsearlo como JSON lanza un error. Y 3xx no es un error: 301 es permanente, 302 y 307 son temporales, y 304 Not Modified es una validación de caché correcta que muchos clientes tratan por error como un fallo.

400 Bad Request: el servidor no pudo entenderte

400 significa que la petición estaba mal formada a un nivel que el servidor no pudo superar: JSON roto, una query string incorrecta, una cabecera ilegible o un cuerpo que no coincide con el Content-Type declarado. El servidor ni siquiera llegó a tu lógica de negocio.

La causa más común en la práctica es un cuerpo que no es lo que promete la cabecera: enviar JSON mientras la librería usa por defecto application/x-www-form-urlencoded, u olvidar serializar un objeto.

curl -i -X POST https://api.ejemplo.com/usuarios \
  -H 'Content-Type: application/json' \
  -d '{"nombre": "Ada",}'

HTTP/1.1 400 Bad Request
{"error": "Expected double-quoted property name in JSON at position 17"}

Esa coma final hace que el JSON sea inválido, así que la petición muere antes de llegar a ningún handler. Si el cuerpo de un 400 menciona una posición, trátalo como un problema de sintaxis en lo que enviaste, no como un valor rechazado.

401 frente a 403: la pareja que más se confunde

401 Unauthorized significa que el servidor no sabe quién eres: las credenciales faltan, están mal formadas o han expirado. El nombre es un error histórico; trata de autenticación, no de autorización. Un 401 correcto debe incluir una cabecera WWW-Authenticate que indique cómo autenticarse.

403 Forbidden significa que el servidor sabe perfectamente quién eres y aun así no puedes hacer esto. Las credenciales son válidas; falta el permiso. Reintentar con el mismo token nunca ayudará.

La regla práctica: si presentar otras credenciales podría arreglarlo, es 401. Si ninguna credencial que tenga quien llama lo arreglaría, es 403.

# token expirado -> 401: renovar y reintentar
HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer error="invalid_token"

# token válido, permisos insuficientes -> 403: no reintentar
HTTP/1.1 403 Forbidden

Importa más allá del orden. Los clientes suelen renovar el token automáticamente ante un 401. Devolver 401 para un problema de permisos los mete en un bucle de renovación que nunca puede tener éxito, y cada vuelta aparece en tus logs como un nuevo fallo de autenticación.

404 y 410: no está ahora, o se fue para siempre

404 dice que el servidor no encontró nada en esa ruta, sin afirmar si alguna vez existió. Cubre tanto una errata en la URL como un registro borrado.

410 Gone es la versión deliberada: esto existió y no volverá. Para Google la diferencia es menor de lo que se suele creer: su documentación trata igual todos los 4xx salvo el 429 y retira del índice las URL que los devuelven, sean 404 o 410. Aun así, 410 es la respuesta más clara para contenido eliminado a propósito, porque dice a quien lo pide que no espere que vuelva.

En las APIs aparece a menudo una decisión: un recurso que existe pero pertenece a otro cliente. Devolver 403 confirma que el ID es real, lo que filtra información. Muchas APIs devuelven 404 para que quien no tiene permiso no pueda sondear IDs válidos.

422 frente a 400: sintaxis contra significado

422 Unprocessable Content es para una petición que el servidor parseó sin problemas y aun así no puede aceptar. El JSON es válido, los campos están, y un valor es incorrecto: un email sin @, una cantidad negativa, una fecha de fin anterior a la de inicio.

Merece la pena mantener la separación. 400 significa «corrige tu serialización»; 422 significa «corrige tus datos». El cliente puede actuar según esa diferencia: un 422 se puede mostrar al usuario campo por campo, mientras que un 400 es un bug del cliente.

HTTP/1.1 422 Unprocessable Content
{
  "errors": {
    "email": ["debe contener @"],
    "fecha_fin": ["debe ser posterior a fecha_inicio"]
  }
}

No todo el mundo sigue esta convención. Muchas APIs conocidas devuelven 400 para errores de validación, así que lee la documentación en lugar de suponer. Si diseñas la API, elige una convención y mantenla; mezclar señales es peor que cualquiera de las dos opciones.

429 Too Many Requests: respeta la cabecera

429 significa que superaste un límite de peticiones. Lo importante no es el código sino la cabecera Retry-After, que indica un número de segundos o una fecha HTTP.

HTTP/1.1 429 Too Many Requests
Retry-After: 30
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1735689600

Reintentar inmediatamente es el error típico, y en muchas pasarelas alarga el bloqueo. Espera lo que diga Retry-After cuando esté presente y, si no, usa un backoff exponencial con jitter para que una flota de clientes no reintente a la vez.

Si recibes 429 sin acercarte al límite documentado, comprueba si el límite es por IP en lugar de por clave. Todo lo que está detrás de un NAT compartido, como runners de CI, la red de una oficina o una región serverless, puede agotar un límite por IP del que ningún cliente individual es responsable.

500: el que nunca deberías devolver a propósito

500 significa un fallo no controlado: una excepción que escapó, un null inesperado, una consulta que explotó. Por diseño no da información a quien llama, porque cualquier detalle podría filtrar datos internos.

Trata cada 500 de tu propio servicio como un bug, no como un estado. Si una situación es esperable, como una validación, un registro inexistente o un límite de peticiones, merece su propio código. Un 500 que ocurre a diario es un handler que olvidaste escribir, y esconde incidentes reales entre ruido que has aprendido a ignorar.

Cuando recibes un 500 de la API de otro, el cuerpo de la respuesta suele ser más útil que el código: muchos servicios incluyen un ID de petición o de traza. Guárdalo, porque suele ser lo único con lo que su equipo de soporte puede trabajar.

502, 503, 504: tres fallos distintos del upstream

Estos tres vienen de un proxy, balanceador o pasarela, no de tu aplicación, y dicen cosas claramente distintas.

502 Bad Gateway: el proxy llegó al upstream y recibió una respuesta que no pudo usar. En la práctica, la aplicación se cayó a mitad de la respuesta, cerró la conexión o murió al arrancar. Mira el log de la aplicación, no el del proxy.

503 Service Unavailable: el servidor está en marcha pero rechaza peticiones a propósito, por modo mantenimiento, porque no hay backends sanos o porque el pool de conexiones está lleno. Suele venir con Retry-After. Es el código correcto durante un despliegue.

504 Gateway Timeout: el upstream era accesible pero no respondió a tiempo. Casi siempre es una consulta lenta, una llamada externa que bloquea o un timeout del proxy más corto que el tiempo real de respuesta.

# nginx: el código depende de cómo falla
502  el upstream no envió una respuesta válida   -> la app se cayó o terminó
503  no hay upstreams disponibles                  -> todos fallan el health check
504  el upstream no respondió a tiempo            -> se superó proxy_read_timeout

Conviene reconocer el patrón «502 tras un despliegue»: el proxy empieza a enviar tráfico antes de que el proceso nuevo termine de arrancar. Una comprobación de disponibilidad (readiness) que retenga el tráfico hasta que la app responda lo elimina.

Acotar el problema con una sola petición

Casi toda la adivinanza desaparece si miras el intercambio completo y no solo el código. curl -i muestra el estado y las cabeceras juntos, y suele bastar.

curl -i -X POST https://api.ejemplo.com/v1/pedidos \
  -H "Authorization: Bearer $TOKEN" \
  -H 'Content-Type: application/json' \
  -d '{"sku":"A-1","cantidad":2}'

Después lee en este orden. ¿Es 4xx o 5xx, cliente o servidor? En un 4xx, ¿hay cabecera WWW-Authenticate (autenticación) o un cuerpo con errores por campo (validación)? En un 5xx, ¿hay Retry-After o una cabecera Server que nombre un proxy? Eso separa un 503 deliberado de una caída.

Dos comprobaciones resuelven una parte sorprendente de los casos confusos. Añade -L para seguir redirecciones, porque un 405 o 404 en un POST suele resultar ser un 301 que convirtió el método en GET sin avisar. Y compara con una petición que sabes que funciona: si ambas fallan igual, el problema es el entorno, no el endpoint.

Fuentes y lecturas recomendadas

RFC 9110 (HTTP Semantics) define todos los códigos de estado citados, incluida la cabecera WWW-Authenticate para 401 y Retry-After. RFC 6585 añadió 429, y RFC 6750 define los errores de tokens Bearer. La documentación de nginx describe cuándo el proxy devuelve 502, 503 y 504, y Google Search Central explica cómo trata 404 y 410.

Frequently asked questions

¿Cuál es la diferencia entre el error 401 y el 403?

401 significa que el servidor no puede identificarte: faltan credenciales, son incorrectas o expiraron, y otras credenciales podrían arreglarlo. 403 significa que te identificó y aun así no tienes permiso, así que reintentar con el mismo token no funcionará.

¿Qué significa el código de error HTTP 404?

Que el servidor no encontró nada en esa URL. Puede ser una errata en la ruta o un recurso eliminado. Si el contenido se eliminó para siempre, `410 Gone` es más preciso.

¿Los errores de validación deben devolver 400 o 422?

422 es más preciso: la petición se parseó bien y un valor no es aceptable. 400 es para lo que el servidor no pudo parsear, como JSON mal formado. Muchas APIs usan 400 para ambos, así que consulta su documentación.

¿Qué causa un error 502 Bad Gateway?

Un proxy llegó a tu aplicación y recibió una respuesta inutilizable, normalmente porque el proceso se cayó o aún no había arrancado. Revisa el log de la aplicación; tras un despliegue suele indicar que el tráfico llegó antes de que el proceso estuviera listo.

¿Por qué recibo 429 si no supero el límite?

El límite suele aplicarse por IP y no por clave de API. Detrás de un NAT compartido, varios clientes pueden agotarlo juntos. Respeta `Retry-After` y usa backoff con jitter.

¿El código 304 Not Modified es un error?

No. Es una validación de caché correcta que indica que la copia guardada sigue vigente, y no tiene cuerpo por diseño. El código que trata todo lo que no es 200 como fallo lo reporta mal.