412 Precondition Failed
Uso
Cuando se recibe el mensaje de error 412 Precondition Failed, el cliente sabe que una o varias condiciones indicadas en la solicitud no se cumplieron y que el recurso no se encontraba en el estado esperado. El comportamiento concreto depende de la cabecera de precondición. Un fallo de If-Match devuelve 412 para cualquier método. Un fallo de If-None-Match devuelve 304 para GET y HEAD, y 412 para todos los demás métodos.
Esto es parecido al 304, aunque en el caso del 304 el recurso no se envió porque el cliente ya disponía de la versión más reciente y una transmisión adicional resulta innecesaria.
Impacto en SEO
Los motores de búsqueda como Google no indexan un URL que responde con el estado 412. Los URL indexados anteriormente se eliminan de los resultados de búsqueda.
Ejemplo
El cliente quiere responder a una entrada de blog, pero solo con la condición de que la entrada no haya cambiado desde la última vez que el cliente consultó el recurso. La condición se incluye en la cabecera If-Unmodified-Since. Cuando la condición no se cumple, el recurso ha sido modificado y el servidor responde con 412 Precondition Failed para indicar que la solicitud no se completó.
Al seguir este protocolo se evita el problema de la “actualización perdida”. Las actualizaciones perdidas se producen cuando varios clientes escriben en el mismo recurso y uno o más de ellos trabajan con una versión desactualizada. Al garantizar que el recurso no ha sido modificado, se impide sobrescribir datos que se hayan cambiado entretanto.
Solicitud
POST /blog/update?postid=111&task=reply HTTP/1.1
Host: example.com
If-Unmodified-Since: Fri, 1 Jan 2021 00:00:00 GMT
Content-Type: text/plain
Content-Length: 45
<Message body contains reply-text from the client>
Respuesta
HTTP/1.1 412 Precondition Failed
Content-Type: text/html
Content-Length: 139
<html>
<head>
<title>Blog Update Error</title>
</head>
<body>
<p>The post has changed since last viewed.</p>
</body>
</html>
Cómo solucionarlo
Obtenga la versión actual del recurso con una solicitud GET o HEAD para conseguir el valor más reciente de ETag o Last-Modified. Sustituya el validador obsoleto de la cabecera If-Match o If-Unmodified-Since por el actual y reintente la solicitud.
Cuando se utiliza control de concurrencia optimista (habitual en las API REST y en las plataformas CMS), el flujo de trabajo es el siguiente:
Borrar la caché del navegador resuelve los errores 412 causados por validadores almacenados en caché que están obsoletos. El navegador conserva un ETag o una marca de tiempo desactualizados y adjunta automáticamente cabeceras condicionales antiguas a las solicitudes.
Los proxys de caché y las CDN situados entre el cliente y el servidor de origen a veces entregan validadores obsoletos. Verifique que las configuraciones del proxy transmiten las cabeceras condicionales sin modificarlas. AWS CloudFront, por ejemplo, devuelve 412 cuando una cabecera If-Match de una solicitud GET no coincide con el ETag del objeto. En estos casos, confirme que la caché de la CDN esté actualizada o invalide la distribución.
412 frente a 409
Tanto 412 como 409 indican un conflicto de estado, pero la causa es distinta. Una respuesta 412 significa que una cabecera de precondición (If-Match o If-Unmodified-Since) se evaluó como falsa. Una respuesta 409 significa que la solicitud entra en conflicto con el estado actual del recurso, al margen de las cabeceras condicionales. Utilice 412 para las comprobaciones de concurrencia optimista basadas en ETag o marcas de tiempo. Utilice 409 para los conflictos de lógica de negocio, como entradas duplicadas o transiciones de estado no válidas.
Referencias de código
.NET
HttpStatusCode.PreconditionFailed
Rust
http::StatusCode::PRECONDITION_FAILED
Rails
:precondition_failed
Go
http.StatusPreconditionFailed
Symfony
Response::HTTP_PRECONDITION_FAILED
Python3.5+
http.HTTPStatus.PRECONDITION_FAILED
Java
java.net.HttpURLConnection.HTTP_PRECON_FAILED
Apache HttpComponents Core
org.apache.hc.core5.http.HttpStatus.SC_PRECONDITION_FAILED
Angular
@angular/common/http/HttpStatusCode.PreconditionFailed