Códigos de Estado HTTP: Lo Que Tu Servidor Realmente Le Dice al Cliente
Toda respuesta HTTP incluye un código de estado de tres dígitos. Acertalos y tu API es intuitiva. Errarlos y los desarrolladores pasan horas depurando por qué el cliente hace retry en un 200 que debería ser 400.
Las Cinco Clases
| Rango | Categoría | Significado |
|---|
| 1xx | Informacional | Solicitud recibida, continuando |
| 2xx | Éxito | Solicitud aceptada y procesada |
| 3xx | Redirección | Cliente debe tomar acción adicional |
| 4xx | Error del Cliente | La solicitud tiene un problema |
| 5xx | Error del Servidor | Servidor falló al cumplir solicitud válida |
Códigos de Éxito (2xx)
| Código | Nombre | Cuándo Usar |
|---|
| 200 | OK | GET exitoso, PUT/PATCH actualizó con éxito |
| 201 | Created | POST creó un nuevo recurso. Incluye header Location con la URL del nuevo recurso. |
| 202 | Accepted | Solicitud aceptada para procesamiento asíncrono (ej: job en cola). Aún no completado. |
| 204 | No Content | DELETE exitoso, nada que retornar. También bueno para PUT sin cuerpo de respuesta. |
// Ejemplos Express.js
app.post('/users', (req, res) => {
const user = createUser(req.body);
res.status(201).json(user);
});
app.delete('/users/:id', (req, res) => {
deleteUser(req.params.id);
res.status(204).send();
});
app.post('/reports', (req, res) => {
queueReportGeneration(req.body);
res.status(202).json({ message: 'Reporte siendo generado' });
});
Códigos de Redirección (3xx)
| Código | Nombre | Cuándo Usar |
|---|
| 301 | Moved Permanently | URL del recurso cambió permanentemente. Los browsers cachean esto. Usa para migraciones de dominio. |
| 302 | Found | Redirect temporal. Browser usa GET para el redirect (incluso si el original era POST). |
| 304 | Not Modified | GET condicional — recurso no cambió desde la versión cacheada. |
| 307 | Temporary Redirect | Como 302, pero preserva el método HTTP. POST sigue siendo POST. |
| 308 | Permanent Redirect | Como 301, pero preserva el método HTTP. |
Error común: Usar 302 cuando debería ser 301. Si el cambio de URL es permanente (mudó a nuevo dominio, cambió estructura de URL), usa 301 para que los buscadores actualicen su índice.
Códigos de Error del Cliente (4xx)
| Código | Nombre | Cuándo Usar |
|---|
| 400 | Bad Request | JSON malformado, campos requeridos faltantes, tipos de datos inválidos |
| 401 | Unauthorized | Sin autenticación o token inválido. Nombre engañoso — significa "no autenticado". |
| 403 | Forbidden | Autenticado pero sin permiso. El usuario es conocido, pero no puede acceder a este recurso. |
| 404 | Not Found | Recurso no existe en esta URL. |
| 405 | Method Not Allowed | Endpoint existe, pero no para este método HTTP (ej: POST en endpoint solo GET). |
| 409 | Conflict | Conflicto con estado actual — email duplicado, versión incompatible, edición concurrente. |
| 422 | Unprocessable Entity | JSON es válido, pero datos no pasan validación (ej: formato de email incorrecto). |
| 429 | Too Many Requests | Rate limited. Incluye header Retry-After. |
// 400 vs 422 — ambos son válidos, elige una convención
// 400: solicitud es estructuralmente incorrecta (JSON malformado)
// 422: solicitud es estructuralmente correcta, pero datos son inválidos
app.post('/users', (req, res) => {
if (!req.body.email) {
return res.status(422).json({
error: 'Validación falló',
details: [{ field: 'email', message: 'Email es requerido' }]
});
}
});
Códigos de Error del Servidor (5xx)
| Código | Nombre | Cuándo Usar |
|---|
| 500 | Internal Server Error | Genérico "algo se rompió." No filtres stack traces a clientes. |
| 502 | Bad Gateway | Proxy reverso (Nginx) no pudo conectar al servidor upstream (tu app crasheó). |
| 503 | Service Unavailable | Servidor temporalmente fuera — mantenimiento, sobrecargado. Incluye Retry-After. |
| 504 | Gateway Timeout | Proxy reverso hizo timeout esperando que tu app responda. |
Principio clave: 4xx significa que el cliente necesita corregir algo. 5xx significa que el servidor necesita corregir algo. Si el cliente nunca puede tener éxito cambiando su solicitud, es un 5xx.
Árbol de Decisión
¿La solicitud es válida?
├── No → ¿Es un problema estructural?
│ ├── Sí → 400 Bad Request
│ └── No → 422 Unprocessable Entity
├── Sí → ¿El usuario está autenticado?
│ ├── No → 401 Unauthorized
│ └── Sí → ¿El usuario está autorizado?
│ ├── No → 403 Forbidden
│ └── Sí → ¿El recurso existe?
│ ├── No → 404 Not Found
│ └── Sí → ¿Se procesó con éxito?
│ ├── Sí → 200/201/204
│ └── No → 500 Internal Server Error
Referencia rápida: Códigos de Estado HTTP — tabla buscable con descripciones, categorías y ejemplos para cada código.