Back to HTTP Status Codes

428 Precondition Required 4xx

The server requires the request to include a conditional header before it can be processed.

What does 428 mean?

A 428 Precondition Required response means the server requires the request to include a conditional header (like If-Match) before it'll process the operation — but unlike 412 (Precondition Failed, where a precondition was provided but didn't hold), 428 means no precondition was provided at all, and the server insists on one.

This code exists specifically to address the "lost update" problem: if a client can update a resource without first proving it has the current version (via If-Match and an ETag), two clients editing the same resource concurrently could silently overwrite each other's changes. 428 lets a server enforce "you must tell me what version you think you're updating" as a requirement, not just an optional best practice.

How a 428 behaves

  • It can carry a body explaining what precondition is required and how to obtain the necessary information (e.g., "fetch this resource first to get its current ETag, then include it via If-Match")
  • It's not cacheable
  • It's a prerequisite step, not a final rejection — the implication is "do a GET first to get the current state/ETag, then retry your write with the appropriate conditional header"

Common causes

If you're building the API:

  • You've implemented mandatory optimistic concurrency control — write operations (PUT/PATCH/DELETE) on certain resources require an If-Match header with a current ETag, and a client attempted a write without one
  • You're protecting against lost-update scenarios on resources where concurrent edits are likely (collaborative tools, shared configuration)

If you're calling an API:

  • You're attempting a PUT/PATCH/DELETE on a resource that requires conditional headers, without first fetching the resource to obtain its current ETag
  • Your client code was written against an API version that didn't require this, and the API has since added this requirement for certain endpoints

If you're a website visitor:

  • Not applicable directly — this is an API-level concurrency-control mechanism

How to fix it

As an API/website builder:

  • If requiring preconditions, document this clearly — API consumers need to know they must GET a resource (to obtain its ETag) before they can PUT/PATCH/DELETE it
  • Provide clear guidance in the 428 response body about what's required and how to obtain it

As an API consumer:

  • GET the resource first to obtain its current ETag (or Last-Modified value), then include the appropriate conditional header (If-Match with the ETag, or If-Unmodified-Since with the timestamp) on your write request
  • If you're caching resource representations, ensure you're using a current ETag — a stale one would result in 412 (precondition failed) rather than 428 (precondition missing), since you'd be providing a precondition, just not the current one

As a website visitor:

  • Not applicable

428 vs 412

428 Precondition Required 412 Precondition Failed
What's missing/wrong No conditional header was provided at all A conditional header was provided, but it doesn't match the resource's current state
Typical fix Fetch the resource to get its current ETag, then retry with If-Match Re-fetch the resource (it changed since your last fetch), then retry with the new ETag

Real-world examples

APIs implementing strict optimistic concurrency control for shared/collaborative resources may require If-Match on all PUT/PATCH/DELETE requests to those resources, returning 428 for any write attempt that omits it — effectively forcing every client to "check before you write," reducing the chance of silent lost updates across an entire API surface rather than relying on individual clients to opt into this safety mechanism voluntarily.

SEO implications

None — 428 is exclusively relevant to conditional API write operations.

FAQ

What's the difference between 428 and 412?

428 means you didn't provide a required conditional header at all. 412 means you did provide one, but it didn't match the resource's current state (meaning the resource changed since you last fetched it).

How do I resolve a 428?

Fetch the resource first (a normal GET) to obtain its current ETag or Last-Modified value, then retry your write request including the appropriate conditional header (If-Match or If-Unmodified-Since) with that value.

Why would an API make conditional headers mandatory rather than optional?

To guarantee protection against lost updates across all clients, rather than relying on individual client implementations to voluntarily use conditional headers — a mandatory requirement ensures the safety mechanism can't be accidentally skipped.

Is 428 common in REST APIs?

It's used by some APIs that prioritize strict concurrency safety for shared/collaborative resources, but it's not a universal requirement — many APIs make conditional headers optional (supporting 412 if provided and incorrect, but not requiring them at all) rather than mandatory (428 if absent).

Does 428 apply to GET requests?

No — preconditions in this context (If-Match, If-Unmodified-Since) are relevant to write operations (PUT/PATCH/DELETE) where overwriting concerns apply. GET requests don't modify anything, so this requirement wouldn't apply to them.

Fun fact

428 was introduced in the same 2012 RFC (RFC 6585) as 429 — both codes address relatively modern concerns (rate limiting and concurrency safety, respectively) that became increasingly important as HTTP evolved from primarily serving static documents toward powering complex, concurrent, multi-client applications — a good example of how newer status codes tend to cluster around the practical challenges of interactive APIs rather than simple document retrieval.

Related Status Codes