In This Article

Back to blog

412 Precondition Failed: What It Means and How to Fix It

Errors

What the HTTP 412 Precondition Failed error means, how conditional headers like If-Match trigger it, and how to fix it on the client and the server.

Nerijus Kriaučiūnas

Last updated - ‐ 8 min read

Key Takeaways

  • A 412 HTTP status code means the server rejected the request because header conditions were not met.

  • Servers use it to prevent collisions when multiple clients edit the same data.

  • You can fix it by fetching the latest version of the resource, merging your changes into it, and retrying with the new ETag.

The 412 Precondition Failed error message happens when a request includes conditional headers like If-Match, but the server evaluates them as false. It usually means someone else modified the resource since you last accessed it.

This guide explains how to troubleshoot the error when calling external APIs and shows you when to return it in your own applications.

What Is the 412 Precondition Failed Error?

The 412 Precondition Failed HTTP status code indicates the server evaluated a condition in the request header and found it to be false, so the conditional request failed. The server checks these conditions after its normal request checks, and before it processes the payload, so a 412 doesn’t mean your request body is wrong.

Conditional headers let a client place preconditions on the resource’s current representations, so the server won’t apply the request method if the resource is in an unexpected state.

Developers use the 412 HTTP code to prevent the lost update problem, which happens when two users edit the same record simultaneously and accidentally overwrite each other.

By rejecting the stale update instead of blindly saving the data, the server enforces concurrency control and protects your data integrity.

How Conditional Headers Trigger a 412

In conditional requests, clients use an ETag (entity tag) or a Last-Modified timestamp to check the state of the requested resource before modifying it.

ETags are usually the more reliable validator: a strong ETag changes whenever the data changes, while a Last-Modified timestamp only has one-second resolution, so two edits within the same second can share it.

The status code the server responds with when a condition fails in conditional requests depends on the header and the request method. For example, a GET with If-None-Match or If-Modified-Since gets a 304 Not Modified when the cached copy is still current.

State-changing methods like PUT, POST, or DELETE get a 412 HTTP status code to block stale updates.

Header Client assertion Failure response for GET and HEAD Failure response for other methods (PUT, POST, DELETE)
If-Match The resource tag matches the client value 412 Precondition Failed 412 Precondition Failed
If-None-Match The resource tag differs from the client value 304 Not Modified 412 Precondition Failed
If-Modified-Since The resource changed after this specific time 304 Not Modified Ignored by server
If-Unmodified-Since The resource remained unchanged since this time 412 Precondition Failed 412 Precondition Failed

Ready to get started?
Register now

Common Causes of a 412 Precondition Failed Error

Clients, including web browsers, typically trigger the HTTP error when they send an outdated version of an ETag that no longer matches the server state.

Developers can also cause it during testing by hard-coding an If-Match value without making it dynamic before production.

Timestamp comparisons can also fail when clients or servers mishandle time zones (HTTP dates are always in GMT), or when the server compares a modification time stored with sub-second precision against an HTTP date that only has whole seconds.

Genuine overlapping edits also trigger this HTTP status code when multiple people try to save changes to the same document at the same time.

Infrastructure and Proxy Complications

Content delivery networks and reverse proxies can sometimes strip or rewrite validators as they process incoming traffic. For example, Cloudflare converts strong ETags to weak ones or removes them when it modifies a response, and a weak ETag never passes an If-Match check because If-Match requires a strong comparison.

If you change the ETag generation algorithm during a deployment, every ETag your clients have stored stops matching at once, so their next conditional writes fail with a 412. If you use clients that replay captured headers from old sessions, they will also fail when the recorded validators don’t match the live resource.

A 412 on a standard GET request with no conditional headers attached is anomalous, because there’s no condition to fail. Check the load balancers, reverse proxies, and other layers in front of your app for misconfiguration.

How to Fix a 412 Precondition Failed Error

To diagnose a 412, log the If-Match or If-Unmodified-Since value you sent and compare it with the ETag or Last-Modified value from a fresh GET.

A well-built client catches the 412 response code and sends a fresh GET request to fetch the latest ETag and current data.

The application must then merge the local edits with the fresh server data or prompt the user to review the conflicts. Never blindly retry the original payload with the new ETag, as it defeats the concurrency check and causes a lost update.

Here’s how to do it using Python:

import requests

url = "https://api.example.com/data/1"
local_copy = {"key": "old_value", "title": "Draft"}   # data you fetched earlier
stale_etag = '"old_etag_123"'                         # ETag that came with it
my_changes = {"key": "new_value"}                     # fields you edited

# 1. Send the update with the ETag you have
response = requests.put(url, json={**local_copy, **my_changes},
                        headers={"If-Match": stale_etag}, timeout=10)
print(f"First attempt: {response.status_code}")

if response.status_code == 412:
    # 2. Fetch the current version and its ETag
    fresh = requests.get(url, timeout=10)
    fresh.raise_for_status()
    current_etag = fresh.headers["ETag"]

    # 3. Merge your edits into the current data (or show the conflict to the user)
    merged = {**fresh.json(), **my_changes}

    # 4. Retry with the new ETag
    retry = requests.put(url, json=merged,
                         headers={"If-Match": current_etag}, timeout=10)
    print(f"Retry: {retry.status_code}")

Here's how it works in JavaScript. Set the Content-Type header, because fetch sends a string body as plain text, which JSON APIs can reject with a 415 error :

const url = "https://api.example.com/data/1";
const localCopy = { key: "old_value", title: "Draft" };   // data you fetched earlier
const staleEtag = '"old_etag_123"';                       // ETag that came with it
const myChanges = { key: "new_value" };                   // fields you edited

// 1. Send the update with the ETag you have
let response = await fetch(url, {
    method: "PUT",
    headers: { "Content-Type": "application/json", "If-Match": staleEtag },
    body: JSON.stringify({ ...localCopy, ...myChanges }),
});
console.log(`First attempt: ${response.status}`);

if (response.status === 412) {
    // 2. Fetch the current version and its ETag
    const fresh = await fetch(url);
    const currentEtag = fresh.headers.get("ETag");

    // 3. Merge your edits into the current data (or show the conflict to the user)
    const merged = { ...(await fresh.json()), ...myChanges };

    // 4. Retry with the new ETag
    response = await fetch(url, {
        method: "PUT",
        headers: { "Content-Type": "application/json", "If-Match": currentEtag },
        body: JSON.stringify(merged),
    });
    console.log(`Retry: ${response.status}`);
}

If another client changed the resource since you fetched it, either script prints:

First attempt: 412
Retry: 200

In a browser, cross-origin APIs must list ETag in Access-Control-Expose-Headers, or headers.get("ETag") returns null.

If you run the API, confirm that it only returns a 412 status code for genuine condition failures. Make sure you configure deployment pipelines to keep ETag generation algorithms stable across rollouts to prevent false positives.

You also need to verify that all middleware components pass validators through untouched and establish monitoring alerts to track abnormal spikes in failed requests.

Search engine crawlers send plain or cache-validating GET requests, and a correctly configured server responds with a 200 or 304, never a 412. A 412 served to a crawler points to a server or proxy misconfiguration, and Google removes indexed URLs that return a 4xx error code, 412 included, from its search results.

412 vs. 409 vs. 428: When to Use Each

The difference is what the server is responding to: a 412 means a condition the client sent turned out false, a 409 means the request clashes with the resource's current state, and a 428 means the server requires a condition the client didn't send.

  • Use a 412 when a provided header condition evaluates to false.
  • Use a 409 when the request conflicts with the current state of the target resource.
  • Use a 428 when you require the client to provide a condition before the server will process the request.
Status code Server evaluation target Minimal example request
412 Precondition Failed The header condition evaluates to false. PUT /item with If-Match: "old_tag"
409 Conflict The request conflicts with current resource state. POST /users with a duplicate email address
428 Precondition Required The request lacks required conditional headers. PUT /item with no conditional fields attached

The 428 status code is the counterpart to the 412. Return a 428 when a client sends a PUT, PATCH, or DELETE without If-Match or If-Unmodified-Since to a resource where you require them, so every update goes through optimistic concurrency control.

FAQ

What does HTTP 412 mean?

The 412 error code means the server evaluated a condition in the request headers and found it to be false, so it didn't carry out the request.

What causes a 412 Precondition Failed error?

It usually happens when a client sends an outdated ETag or timestamp because another user recently modified the resource. Hard-coded validators, proxies that rewrite ETags, and deployments that change ETag generation can also cause it.

How do I fix a 412 error?

Fetch the latest resource state, merge your local changes with the fresh data, and retry the request using the updated validator.

What is the difference between 412 and 409?

A 412 status code means a header condition failed. A 409 status code means the request conflicts with the current state of the resource.

What is the difference between 412 and 428?

A 412 means the provided condition failed. A 428 means the server requires a conditional header, but the client omitted it.

Can my browser cache cause a 412 error?

Yes. If your frontend application reads an outdated ETag from a local browser cache and attaches it to a state-changing request, the server will reject it.

Why do I only get 412 errors in production?

Production environments often have multiple users editing resources at the same time, which exposes conditions that rarely happen during isolated local testing.

Should I stop sending conditional headers to avoid 412?

No. Keep sending conditional requests to prevent silent data overwrites and maintain data integrity.

Create Account
Share on
Article by IPRoyal
Meet our writers
Data News in Your Inbox

No spam whatsoever, just pure data gathering news, trending topics and useful links. Unsubscribe anytime.

No spam. Unsubscribe anytime.

Related articles