Desarrollo web y móvil
Códigos de estado HTTP: referencia completa con su significado y uso correcto
Principiante17 min de lecturaActualizado
Esta página se tradujo del inglés con ayuda de IA y se cotejó con el original. Leer la versión en inglés
El código de estado es lo primero que un cliente sabe de una respuesta y, a menudo, lo único que lee un intermediario, así que elegir el equivocado rompe en silencio cachés, reintentos y manejo de errores. Esta referencia explica el modelo de las cinco clases, recoge en tablas cada código registrado con su significado y su uso adecuado, y después repasa los pares que se confunden, las reglas de redirección y de caché, y cómo elegir códigos al diseñar una API.
Lo que aprenderás
- Explicar la estructura de una línea de estado y qué indica cada una de las cinco clases
- Consultar cualquier código de estado y saber cuándo es la respuesta correcta
- Elegir bien entre los pares que se confunden continuamente, como 301 y 308 o 401 y 403
- Aplicar la semántica de las redirecciones sin cambiar por accidente el método de una petición
- Predecir qué respuestas se pueden almacenar en caché y cómo las peticiones condicionales producen un 304
- Elegir códigos de estado adecuados al diseñar una API HTTP y comunicar los errores de forma coherente
Toda respuesta HTTP empieza con un número de tres cifras, y ese número es la única parte de la respuesta que muchos sistemas llegan a leer. El navegador decide con él si sigue una redirección. La caché, si puede almacenar el cuerpo. El balanceador de carga, si el backend está sano. La biblioteca cliente, si lanza una excepción o reintenta. Elegir el código equivocado no suele provocar un error visible: provoca uno sutil, meses después, cuando un proxy cachea algo que no debía o un bucle de reintentos machaca un endpoint que nunca iba a responder bien.
La especificación de referencia es la RFC 9110, que sustituyó a la antigua familia de la RFC 7231 y hoy es el único documento que describe la semántica de HTTP en las versiones 1.1, 2 y 3. La lista oficial de códigos asignados es el IANA HTTP Status Code Registry. Esta página es un complemento práctico de ambos: qué significa cada código, cuándo es la respuesta correcta y dónde se equivocan los desarrolladores una y otra vez.
La línea de estado y las cinco clases
En HTTP/1.1, una respuesta empieza con la versión del protocolo, el código de estado y una frase de motivo (reason phrase):
HTTP/1.1 404 Not Found
Content-Type: application/problem+json
Content-Length: 91
{"type":"https://example.com/errors/no-such-order","title":"Order not found","status":404}
La frase de motivo es meramente orientativa. Existe para las personas que leen un flujo en bruto, los clientes no deben basarse en ella, y HTTP/2 y HTTP/3 la eliminaron por completo: solo transportan el código numérico como pseudocabecera. No la parsees nunca.
El primer dígito es la parte con significado definido, y funciona como un contrato: un cliente que recibe un código que no conoce debe tratarlo como el x00 genérico de su clase. Una respuesta 499 se trata como 400, y una 572 como 500. Esa regla es la que permite registrar códigos nuevos sin romper los clientes existentes, y es la razón de que inventarse un número no registrado pero verosímil haga menos daño que usar uno registrado con el significado equivocado, aunque sigue siendo peor que acertar.
| Clase | Nombre | Significado |
|---|---|---|
| 1xx | Informational | La petición se ha recibido; el procesamiento continúa. Es una respuesta provisional. |
| 2xx | Successful | La petición se ha recibido, entendido y aceptado. |
| 3xx | Redirection | Hace falta una acción adicional para completar la petición. |
| 4xx | Client error | La petición es defectuosa y volverá a fallar si no se cambia. |
| 5xx | Server error | La petición puede ser válida; el servidor no ha sido capaz de atenderla. |
La frontera entre 4xx y 5xx tiene peso operativo. Es lo que separa «culpa tuya» de «culpa nuestra» en un panel, lo que vigilan los health checks y la base sobre la que se calculan los error budgets. Devolver 500 ante una petición mal formada contamina tus propias alertas; devolver 400 ante un fallo interno oculta una caída real.
1xx — Informational
Respuestas provisionales, enviadas antes de la definitiva. Un cliente que no entiende un 1xx debe ignorarlo y seguir esperando.
| Código | Nombre | Cuándo aparece |
|---|---|---|
| 100 | Continue | El cliente envió Expect: 100-continue solo con las cabeceras, para preguntar si puede mandar un cuerpo grande. El servidor responde 100 para invitarle a hacerlo, o un 4xx para rechazarlo antes de que empiece la subida. |
| 101 | Switching Protocols | El servidor acepta cambiar de protocolo, como en el handshake de WebSocket tras Upgrade: websocket. |
| 102 | Processing | Respuesta de espera de WebDAV para operaciones largas. Obsoleta; usa 103 o mantén la conexión abierta. |
| 103 | Early Hints | Se envía antes de la respuesta final con cabeceras Link para que el navegador pueda precargar recursos o preconectar mientras el servidor sigue trabajando. |
Early Hints es el único de los cuatro que vale la pena añadir a propósito a una aplicación moderna: en una página lenta renderizada en el servidor, permite al navegador empezar a descargar hojas de estilo y fuentes antes de que exista el HTML.
2xx — Successful
| Código | Nombre | Cuándo usarlo |
|---|---|---|
| 200 | OK | El éxito por defecto. El cuerpo contiene el resultado de la petición. |
| 201 | Created | Se ha creado un recurso nuevo. Incluye una cabecera Location que apunte a él y, normalmente, su representación en el cuerpo. |
| 202 | Accepted | La petición se ha aceptado para procesarla de forma asíncrona y todavía no se conoce el resultado. Devuelve una forma de consultar el progreso. |
| 203 | Non-Authoritative Information | Un proxy que transforma el contenido ha modificado la carga útil. Poco habitual fuera de los intermediarios. |
| 204 | No Content | Éxito sin nada que devolver: un DELETE, o un PUT cuyo estado resultante ya conoce el cliente. No debe haber cuerpo. |
| 205 | Reset Content | Éxito; el cliente debe limpiar el formulario que originó la petición. Se usa poco. |
| 206 | Partial Content | Una petición Range satisfactoria. Es la base de las descargas reanudables y del avance y retroceso en contenido multimedia. |
| 207 | Multi-Status | WebDAV: un cuerpo XML con varios códigos de estado independientes. |
| 208 | Already Reported | WebDAV: los miembros de una colección ya se enumeraron. |
| 226 | IM Used | La respuesta es el resultado de aplicar instance manipulations, como una codificación delta. |
El par que importa en el día a día es 201 frente a 200. Si la petición ha creado algo direccionable, dilo y da su dirección: así clientes y cachés pueden trabajar con el nuevo recurso sin adivinar su URL. Y 204 significa exactamente sin cuerpo: enviar JSON con él viola la especificación, y algunos clientes reaccionan lanzando una excepción.
3xx — Redirection
| Código | Nombre | ¿Conserva el método? | Cuándo usarlo |
|---|---|---|---|
| 300 | Multiple Choices | n/a | Existen varias representaciones y el cliente debe elegir. Casi nunca se usa. |
| 301 | Moved Permanently | No, en la práctica | El recurso tiene una nueva URL permanente. Históricamente convierte POST en GET. |
| 302 | Found | No, en la práctica | Traslado temporal. También convierte POST en GET en todos los clientes reales. |
| 303 | See Other | No, por diseño | «Obtén el resultado en esta otra URL con GET». La respuesta correcta tras enviar con éxito un formulario. |
| 304 | Not Modified | n/a | La copia en caché sigue siendo válida; no hay cuerpo. Respuesta a una petición condicional. |
| 305 | Use Proxy | n/a | Obsoleto por motivos de seguridad. No lo uses. |
| 306 | (sin uso) | n/a | Reservado; antes era «Switch Proxy». |
| 307 | Temporary Redirect | Sí | Traslado temporal conservando el método y el cuerpo. |
| 308 | Permanent Redirect | Sí | Traslado permanente conservando el método y el cuerpo. |
Cómo acertar con las redirecciones
Merece la pena entender el lío histórico, porque todavía condiciona el comportamiento. La RFC 2616 decía que 301 y 302 no debían cambiar el método de la petición sin confirmación del usuario. Los navegadores lo ignoraron y convirtieron en GET los POST redirigidos, así que la especificación acabó cediendo y documentando el comportamiento real. Eso dejó sin forma de decir «se ha movido, y conserva el método», y por eso se añadieron 307 y 308.
Las reglas prácticas:
- 301 frente a 308. Ambos son permanentes y ambos los cachean los clientes, a menudo indefinidamente, así que ninguno debe usarse a la ligera. Elige 301 para las migraciones de sitio habituales, las redirecciones al host y al esquema canónicos y las páginas que cambian de dirección, porque tiene soporte universal y los buscadores llevan décadas construyendo su comportamiento en torno a él. Elige 308 cuando el endpoint acepta
POST,PUToPATCHy el método tiene que sobrevivir, como en un cambio de ruta de una API versionada. - 302 frente a 307. Ambos son temporales. 302 convertirá un
POSTenGET; 307, no. Si rediriges el envío de un formulario y quieres que el navegador repita elPOSTen la nueva ubicación, necesitas 307. Si quieres el comportamiento seguro, 303 lo declara explícitamente en lugar de depender de una regla de facto. - 303 después de una escritura. El patrón Post/Redirect/Get existe para evitar que recargar el navegador vuelva a enviar un pago. Responde al
POSTsatisfactorio con un 303 y unLocationque apunte a la página de confirmación.
Dos advertencias operativas. Las redirecciones permanentes son difíciles de deshacer: un navegador que ha cacheado un 301 puede tardar muchísimo en volver a preguntar, así que prueba primero con un código temporal y pásalo a permanente cuando estés seguro. Y cada redirección cuesta un viaje de ida y vuelta: las cadenas de tres o cuatro saltos, habituales después de varias migraciones, son un problema de rendimiento medible y conviene reducirlas a un único salto. Cuando depuras una redirección que estropea los parámetros de la query string, el codificador/decodificador de URL hace visible la diferencia al instante.
4xx — Client error
| Código | Nombre | Cuándo usarlo |
|---|---|---|
| 400 | Bad Request | La petición está mal formada: sintaxis inválida, cuerpo imposible de parsear, framing incorrecto. No es un error genérico para todo. |
| 401 | Unauthorized | Falta la autenticación o no es válida. Debe incluir WWW-Authenticate. |
| 402 | Payment Required | Reservado; algunas APIs lo usan para fallos de cuota o de facturación. |
| 403 | Forbidden | El servidor ha entendido la identidad y se niega. Volver a autenticarse no servirá. |
| 404 | Not Found | No hay ningún recurso en esta URL, o el servidor prefiere no decir si existe. |
| 405 | Method Not Allowed | La URL existe, pero no para este método. Debe incluir una cabecera Allow. |
| 406 | Not Acceptable | Ninguna representación coincide con las cabeceras Accept. |
| 407 | Proxy Authentication Required | Como 401, pero para un proxy; usa Proxy-Authenticate. |
| 408 | Request Timeout | El cliente tardó demasiado en enviar la petición. Se puede reintentar sin riesgo. |
| 409 | Conflict | La petición entra en conflicto con el estado actual: una clave duplicada, una edición concurrente, una transición de estado no válida. |
| 410 | Gone | Eliminado deliberadamente y para siempre. Más contundente y útil que 404 cuando lo sabes. |
| 411 | Length Required | La petición necesita un Content-Length. |
| 412 | Precondition Failed | No se cumplió una precondición If-Match o If-Unmodified-Since. Es la base del bloqueo optimista. |
| 413 | Content Too Large | El cuerpo supera el límite del servidor. Antes se llamaba «Payload Too Large». |
| 414 | URI Too Long | El destino de la petición supera el límite del servidor. |
| 415 | Unsupported Media Type | El Content-Type no es uno de los que acepta el endpoint. |
| 416 | Range Not Satisfiable | El rango de bytes solicitado queda fuera del recurso. |
| 417 | Expectation Failed | No se puede satisfacer la cabecera Expect. |
| 418 | I'm a teapot | Una broma del 1 de abril de 1998, reservada por la IANA para que nunca se asigne. |
| 421 | Misdirected Request | La conexión llegó a un servidor que no puede generar una respuesta para esta autoridad. Aparece con el connection coalescing de HTTP/2. |
| 422 | Unprocessable Content | Sintácticamente válida, semánticamente incorrecta. La opción habitual para los fallos de validación de reglas de negocio. |
| 423 | Locked | WebDAV: el recurso está bloqueado. |
| 424 | Failed Dependency | WebDAV: ha fallado una petición de la que esta depende. |
| 425 | Too Early | El servidor no procesará una petición con early data que podría ser una repetición. |
| 426 | Upgrade Required | El cliente debe cambiar de protocolo, por ejemplo a TLS. |
| 428 | Precondition Required | El servidor exige una petición condicional para evitar actualizaciones perdidas. |
| 429 | Too Many Requests | Límite de peticiones alcanzado (rate limiting). Incluye Retry-After. |
| 431 | Request Header Fields Too Large | Las cabeceras superan el límite; normalmente por una cookie demasiado grande. |
| 451 | Unavailable For Legal Reasons | Bloqueado por una exigencia legal. El número es un guiño a Fahrenheit 451. |
5xx — Server error
| Código | Nombre | Cuándo usarlo |
|---|---|---|
| 500 | Internal Server Error | Un fallo no controlado. El cajón de sastre; nunca la respuesta deliberada a una situación conocida. |
| 501 | Not Implemented | El servidor no admite el método en absoluto. |
| 502 | Bad Gateway | Un gateway recibió una respuesta no válida de un servidor upstream. |
| 503 | Service Unavailable | Incapaz temporalmente de atender la petición, por sobrecarga o mantenimiento. Incluye Retry-After. |
| 504 | Gateway Timeout | Un gateway no recibió a tiempo la respuesta de un servidor upstream. |
| 505 | HTTP Version Not Supported | No se admite la versión del protocolo de la petición. |
| 506 | Variant Also Negotiates | Un error de configuración en la negociación de contenido. |
| 507 | Insufficient Storage | WebDAV: no hay espacio suficiente para completar la petición. |
| 508 | Loop Detected | WebDAV: un bucle infinito durante el procesamiento. |
| 510 | Not Extended | Obsoleto; el mecanismo de extensiones al que pertenecía nunca llegó a adoptarse. |
| 511 | Network Authentication Required | Un portal cautivo que exige iniciar sesión. Debe generarlo el proxy que intercepta, nunca un servidor de origen. |
Los pares que se confunden continuamente
401 frente a 403. 401 dice «no sé quién eres, o he rechazado tus credenciales» y obliga al servidor a enviar WWW-Authenticate. 403 dice «sé quién eres y la respuesta sigue siendo no». La consecuencia práctica está en el comportamiento del cliente: un 401 debe desencadenar un aviso de inicio de sesión o una renovación del token; un 403, no. Muchas APIs devuelven 403 cuando el token ha caducado, lo que deja a los clientes en un estado de fallo permanente en lugar de renovarlo.
Hay una tercera opción que conviene conocer. Cuando incluso la existencia de un recurso es confidencial, devolver 404 a quien no está autorizado es una medida de hardening legítima: evita confirmar que existe un objeto con ese identificador.
400 frente a 422. 400 tiene que ver con la forma; 422, con el significado. Un cuerpo que no es JSON válido es un 400; un cuerpo JSON válido cuyo endDate es anterior a su startDate es un 422. Algunas APIs muy conocidas usan 400 para todo y siguen siendo perfectamente utilizables, así que lo verdaderamente importante es la coherencia, junto con un cuerpo que nombre los campos problemáticos. La RFC 9457 define justo para esto un media type estándar de problem details, que vale la pena adoptar para que los clientes interpreten los errores de manera uniforme.
404 frente a 410. 404 significa «aquí no hay nada, y no digo si alguna vez lo hubo». 410 significa «esto existió y se eliminó deliberadamente». Si sabes que el recurso ha desaparecido para siempre, 410 es más informativo: los rastreadores descartan la URL antes y los clientes pueden dejar de reintentar.
409 frente a 422 frente a 412. 409 es un conflicto con el estado actual del recurso: crear un usuario cuyo email ya existe, o cambiar de estado un pedido que ya se ha enviado. 422 es un problema del contenido enviado por sí solo. 412 es la respuesta a una precondición fallida en una petición condicional, que es como se implementa el control de concurrencia optimista con ETag e If-Match.
502 frente a 503 frente a 504. Los tres suelen venir de un proxy y no de tu aplicación, y distinguirlos es la forma más rápida de localizar un incidente. 502 significa que el upstream respondió algo ininteligible, a menudo por un proceso caído o una incompatibilidad de protocolo. 503 significa que el servidor está levantado y se niega deliberadamente en este momento: sobrecargado, en mantenimiento o sin ningún backend sano en el pool. 504 significa que el upstream no respondió a tiempo, lo que apunta a una dependencia lenta, un deadlock o un timeout configurado más bajo en el proxy que en la aplicación. Cuando hay un gateway delante de tus servicios, su propia configuración de timeouts y reintentos determina cuál de los tres ven tus usuarios; consulta gestión de APIs y gateways.
200 con un error dentro. El antipatrón más dañino de la lista. Todos los intermediarios, todos los sistemas de monitorización y todos los clientes genéricos lo tratan como un éxito, así que los fallos desaparecen de los paneles y la lógica de reintento nunca se dispara. Pon el resultado en el código de estado y el detalle en el cuerpo.
Caché y peticiones condicionales
La RFC 9111 define qué respuestas se pueden almacenar, y el código de estado es el punto de partida. Son cacheables por defecto (es decir, una caché compartida puede almacenarlas aunque no haya un Cache-Control explícito) los códigos 200, 203, 204, 206, 300, 301, 308, 404, 405, 410, 414 y 501. Todos los demás requieren permiso explícito mediante directivas de caché. Esa lista trae dos sorpresas que conviene interiorizar: 404 y 301 son cacheables por defecto, así que un despliegue roto temporalmente que devuelve 404, o una redirección publicada por error, pueden quedar recordados en cachés y navegadores mucho después de que corrijas el origen.
En la práctica no deberías fiarte de los valores por defecto. El Cache-Control de la respuesta lo deja claro: no-store prohíbe cualquier almacenamiento, no-cache permite almacenar pero obliga a revalidar antes de reutilizar, private lo restringe al navegador, max-age fija la frescura en segundos y must-revalidate prohíbe servir una copia caducada tras la expiración.
Las peticiones condicionales son el origen del 304. El servidor envía un validador (un ETag o un Last-Modified) y el cliente lo devuelve en la siguiente petición:
GET /api/orders/42 HTTP/1.1
Host: example.com
If-None-Match: "a3f1c0"
HTTP/1.1 304 Not Modified
ETag: "a3f1c0"
Cache-Control: max-age=60
Un 304 no tiene cuerpo, y esa es precisamente la gracia: el cliente ya tiene el contenido y solo necesitaba saber que sigue siendo válido. Los mismos validadores sirven para If-Match en las escrituras, lo que convierte una condición de carrera por actualizaciones perdidas en un 412 limpio.
Cómo elegir códigos al diseñar una API
Un conjunto pequeño y elegido con intención es mejor que una cobertura exhaustiva. La mayoría de las APIs bien diseñadas usan una docena de códigos, más o menos: 200, 201, 202, 204, 301 o 308 para endpoints trasladados, 400, 401, 403, 404, 409, 422, 429, y 500 junto con 503 para el funcionamiento degradado. Déjalos por escrito como parte del contrato de la API para que todos los endpoints se comporten igual y los clientes puedan gestionar los errores de forma genérica.
Más allá de la elección en sí, algunos hábitos compensan. Devuelve un cuerpo de error legible por máquina con una forma única y documentada (problem details de la RFC 9457, con type, title, status, detail y los errores por campo que haya) para que un cliente pueda mostrar un mensaje sin tratar cada endpoint como un caso especial. No filtres stack traces ni identificadores internos en ese cuerpo: regístralos en los logs con un correlation id y devuelve ese id. Envía Retry-After con 429 y 503 para que los clientes esperen con criterio en lugar de adivinar, y asegúrate de que tu propio código cliente lo respeta. Incluye Allow con 405 y WWW-Authenticate con 401, dos cabeceras que exige la especificación. Prefiere las operaciones idempotentes siempre que puedas, porque la idempotencia es lo que hace seguro reintentar un 500 o un 504. Y recuerda que fetch en el navegador se resuelve con cualquier estado, así que el código cliente debe comprobarlo explícitamente, algo que se explica en los fundamentos de JavaScript, mientras que el HttpClient de Angular sigue la convención contraria y lanza un error.
Por último, trata los códigos de estado como un instrumento operativo y no solo como un contrato con los clientes. Grafica por separado las tasas de 4xx y de 5xx, alerta sobre los 5xx y vigila los códigos concretos que delatan algo estructural: un aumento de 401 tras un despliegue suele indicar un problema de tokens o de reloj, un aumento de 504 indica que una dependencia se ha ralentizado, y un aumento de 429 apunta a un cliente abusivo o a un límite demasiado estricto. Las consideraciones de diseño más amplias que hay detrás de estas decisiones se tratan en diseño de APIs REST, y la parte del framework en crear APIs REST con Spring Boot.
Ideas clave
- El primer dígito es el contrato: los códigos desconocidos se tratan como el
x00genérico de su clase, y la división entre 4xx y 5xx decide en tus paneles de quién es la culpa de un incidente. - Usa 307 y 308 cuando una redirección deba conservar el método y el cuerpo; 301 y 302 convierten en la práctica
POSTenGET, y 303 lo declara explícitamente tras una escritura. - 401 significa «autentícate» y 403 significa «no»; 400 significa mal formada y 422 semánticamente inválida; 502, 503 y 504 distinguen un upstream roto, un servidor que se niega y uno lento.
- Un 200 con un error en el cuerpo anula cachés, reintentos y monitorización: pon el resultado en la línea de estado y el detalle en un cuerpo problem details de la RFC 9457.
- 200, 301, 404 y algunos más son cacheables por defecto, así que una redirección errónea o un 404 pasajero pueden sobrevivir al despliegue que los causó, salvo que
Cache-Controldiga otra cosa. - Elige una docena de códigos, documéntalos en el contrato de la API, envía siempre
Retry-After,AllowyWWW-Authenticatedonde se exigen y comprueba el código explícitamente en el código cliente.
Fuentes y lecturas recomendadas
Preguntas frecuentes
- ¿Qué diferencia hay entre 401 y 403?
- 401 Unauthorized significa que la petición no llevaba una autenticación válida o que las credenciales enviadas no se aceptaron; la respuesta debe incluir una cabecera WWW-Authenticate que indique al cliente cómo autenticarse, y reintentar con mejores credenciales puede funcionar. 403 Forbidden significa que el servidor sabe quién eres y aun así se niega. Repetir un 403 con la misma identidad no funcionará nunca, así que el cliente no debería volver a pedir el inicio de sesión.
- ¿Un error de validación debe devolver 400 o 422?
- Usa 400 Bad Request cuando la propia petición está mal formada: JSON roto, una cabecera obligatoria ausente, un parámetro que no se puede interpretar. Usa 422 Unprocessable Content cuando la sintaxis es correcta pero el contenido es semánticamente erróneo, como una fecha de fin anterior a la de inicio o una divisa que la cuenta no puede usar. Ambos son legítimos; lo que más importa es que tu API sea coherente y devuelva un cuerpo legible por máquina que explique qué campo ha fallado.
- ¿Es aceptable devolver 200 con un error en el cuerpo?
- Técnicamente está permitido y casi siempre es un error. Cachés, proxies, políticas de reintento, las devtools del navegador, los paneles de monitorización y las bibliotecas cliente se guían por el código de estado, así que un fallo disfrazado de 200 se vuelve invisible para todas las capas salvo el código que interpreta tu envoltorio concreto. Usa el código de estado para el resultado y el cuerpo para el detalle, idealmente en el formato problem details de la RFC 9457.
- ¿Qué códigos de estado debería reintentar un cliente?
- Reintenta ante 408, 429, 502, 503 y 504, porque indican una condición transitoria, y respeta cualquier cabecera Retry-After antes de aplicar un backoff exponencial con jitter. No reintentes el resto de códigos 4xx: describen un problema de la petición que seguirá ahí la próxima vez. Reintentar un 500 solo es seguro si la operación es idempotente, ya que el servidor puede haberla aplicado a medias.