Back to HTTP Status Codes

412 Precondition Failed 4xx

A condition specified in the request's headers was not met by the current state of the resource.

What does 412 mean?

A 412 Precondition Failed response means the client included one or more conditional headers (If-Match, If-Unmodified-Since, If-None-Match, If-Modified-Since) asserting something about the current state of the resource, and that assertion turned out to be false. The request was otherwise valid — it's the precondition the client attached to it that didn't hold.

This is closely related to 304 (Not Modified), but represents the opposite outcome for a different kind of conditional request: where 304 is the "good news" response to If-None-Match/If-Modified-Since (used for caching — "you already have the latest, nothing to do"), 412 is the "stop" response to If-Match/If-Unmodified-Since (used for safe writes — "you're trying to modify this based on an outdated assumption about its state, so I won't proceed").

How a 412 behaves

  • It's triggered by conditional request headers — without an If-Match, If-Unmodified-Since, or similar header, 412 can't occur, since there's no precondition to fail
  • It typically doesn't include the resource's current state in the body — unlike 409, which often explains the conflict in detail, 412 is more of a blunt "your precondition didn't hold"
  • It's not cacheable
  • It prevents the requested operation from happening at all — unlike 409 (which means the operation conflicts with current state but the server processed the request to discover this), 412 means the server checked the precondition before attempting the operation and stopped immediately

Common causes

If you're building the API or website:

  • A client sent If-Match: <etag> with a PUT/PATCH/DELETE request, asserting "only do this if the resource's current ETag matches this value" — and the resource has since changed, so the ETags don't match
  • A client sent If-Unmodified-Since: <date> asserting "only proceed if this hasn't changed since this date" — and it has changed since then

If you're calling an API:

  • You fetched a resource, got its ETag, then tried to update it with If-Match: <that etag> — but someone else modified the resource in the meantime, so the current ETag no longer matches what you have
  • You're implementing optimistic concurrency control and a 412 is the expected, correct response when a conflicting update has occurred since you last fetched the resource

If you're a website visitor:

  • Essentially never encountered directly — conditional requests with If-Match/If-Unmodified-Since are an API/application-layer mechanism, not something that surfaces during normal browsing

How to fix it

As an API/website builder:

  • If you support conditional requests with If-Match for safe updates (a good practice for preventing lost-update problems), returning 412 when the precondition fails is exactly correct — this is the mechanism working as intended
  • Consider whether your 412 response should include the resource's current state (and current ETag), so the client can immediately see what changed without an additional GET request

As an API consumer:

  • A 412 on a conditional update means the resource changed since you last fetched it — re-fetch the current version, reconcile your intended changes with whatever changed, and retry the update with the new ETag/timestamp
  • This is the expected behavior for optimistic concurrency control — handle it as "someone else updated this, let's reconcile" rather than treating it as an unexpected error

As a website visitor:

  • Not applicable directly — if an application surfaces this as a user-facing message, it likely means "this was changed by someone else since you started editing — please refresh and try again"

412 vs 409 vs 304

Code Trigger Meaning
304 Not Modified If-None-Match/If-Modified-Since on a GET "Your cached copy is still valid — here's nothing new" (used for caching)
412 Precondition Failed If-Match/If-Unmodified-Since on a write (PUT/PATCH/DELETE) "Your assumption about the resource's current state doesn't hold — I won't proceed" (used for safe concurrent writes)
409 Conflict No specific conditional header required "The request conflicts with current state in some way" — a broader category that can overlap with 412's use case, but doesn't require conditional headers

Real-world examples

APIs implementing robust optimistic concurrency control commonly support If-Match with ETag values on update/delete endpoints — a client fetches a resource (receiving its current ETag), then includes If-Match: <etag> on a subsequent PUT, and receives 412 if another client has modified the resource in the meantime, preventing the "lost update" problem where one client's changes silently overwrite another's.

SEO implications

412 is exclusively relevant to conditional API write operations and has no bearing on page-level SEO.

FAQ

What's the difference between 412 and 409?

412 specifically results from a conditional request header (If-Match, If-Unmodified-Since) not matching the resource's current state — it's a precondition check that happens before any operation is attempted. 409 is a broader "this conflicts with current state" response that doesn't require any specific conditional header to trigger.

Is 412 related to 304?

Yes, conceptually — both are outcomes of conditional request headers comparing client-supplied values against a resource's current state. 304 is the "match found, nothing to do" outcome for read-oriented conditional headers (If-None-Match). 412 is the "no match, don't proceed" outcome for write-oriented conditional headers (If-Match).

How do I use If-Match to prevent lost updates?

Fetch the resource, note its ETag header. When updating, include If-Match: <that etag> on your PUT/PATCH/DELETE request. If the resource hasn't changed since your fetch, the update proceeds normally. If it has changed (someone else updated it), you get 412, signaling you should re-fetch and reconcile before retrying.

What should I do after getting a 412?

Re-fetch the current state of the resource (including its new ETag), compare it with the changes you were trying to make, reconcile any conflicts, and retry your update using the new ETag in your If-Match header.

Is implementing 412/If-Match support common?

It's considered a best practice for APIs where concurrent modifications to the same resource are likely (collaborative tools, shared data), but it requires deliberate implementation — many simpler APIs don't support conditional writes and would simply allow the second update to silently overwrite the first (a "last write wins" approach) without any 412.

Fun fact

412 and 304 form a kind of matched pair representing two different philosophies for handling "has this changed?" — 304 optimizes for not re-downloading unchanged data (a read-side concern), while 412 protects against overwriting data that's changed unexpectedly (a write-side concern) — together, they're part of HTTP's relatively under-appreciated built-in toolkit for both performance and data-safety that predates many application-level solutions to the same problems.

Related Status Codes