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 viaIf-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-Matchheader with a currentETag, 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(orLast-Modifiedvalue), then include the appropriate conditional header (If-Matchwith theETag, orIf-Unmodified-Sincewith 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.