You're collaborating on an important document with a colleague using a web-based editor. You both open the same document at the same time. You spend 30 minutes carefully rewriting the introduction while your colleague works on the conclusion. You click "Save" first, and your changes are accepted. Then your colleague clicks "Save" and their version completely overwrites your brilliant new introduction without any warning. Your work has just fallen victim to the "lost update problem."
This frustrating scenario is exactly what the 428 Precondition Required
HTTP status code is designed to prevent. It's one of the more sophisticated and proactive status codes in the HTTP specification, acting as a protective mechanism for resources that might be modified by multiple users simultaneously.
It's not one of the usual suspects, and yet, it plays an incredibly important role in safe, reliable, and concurrent API communication.
So, what exactly does HTTP Status Code 428 Precondition Required mean? When does it appear, and how can you handle it properly?
Think of it as a cautious librarian who won't let you check out a book until you confirm you know which edition you're updating. It's the server's way of saying, "I need you to prove you're working with the most recent version of this resource before I let you make changes."
If you're building collaborative applications, APIs that handle concurrent updates, or any system where data consistency is critical, understanding 428
is essential.
That’s exactly what we'll unpack in this deep-dive, so you can understand not just what 428 means, but why it exists and how it can make your APIs better.
428
responses correctly.Now, let's explore how HTTP 428 Precondition Required solves the problem of conflicting updates.
The Problem: The Dreaded Lost Update
To understand why 428
exists, we need to appreciate the problem it solves. In multi-user systems, when two or more people try to update the same resource at roughly the same time, you can encounter several issues:
- Lost Updates: The classic problem where the second write overwrites the first without incorporating its changes.
- Conflicting Changes: Two users make different changes to different parts of the same resource.
- Stale Data Updates: A user makes changes based on outdated information.
Traditional approaches often rely on the client "doing the right thing" by including conditional headers. But what if the client forgets? The 428
code allows the server to enforce good behavior.
What Does HTTP 428 Precondition Required Actually Mean?
The 428 Precondition Required
status code indicates that the origin server requires the request to be conditional. It's the server's way of mandating that the client must include conditional headers (like If-Match
or If-Unmodified-Since
) to prove they're working with fresh data.
The response should include a helpful explanation of what precondition is required. A typical 428
response looks like this:
HTTP/1.1 428 Precondition RequiredContent-Type: application/problem+json
{
"type": "<https://example.com/probs/conditional-required>",
"title": "Precondition Required",
"detail": "This resource requires conditional requests. Please include an If-Match or If-None-Match header.",
"instance": "/articles/123"
}
The server is essentially saying: "For this particular resource, I won't accept blind updates. You need to show me that you know what version you're trying to modify."
In simpler terms, the server expects the client to include a precondition header such as If-Match
or If-Unmodified-Since
before it’s willing to process the request.
If that precondition isn’t included, the server will refuse the request and respond with a 428 Precondition Required error.
Official RFC Definition
The 428 status code is defined in RFC 6585, which introduced several additional HTTP status codes to improve web communication and reliability.
Here’s what it says:
“The 428 (Precondition Required) status code indicates that the origin server requires the request to be conditional. Its purpose is to prevent the 'lost update' problem, where a client GETs a resource’s state, modifies it, and PUTs it back to the server, while a third party has modified the resource in the meantime.”
That’s a lot of technical jargon, but the essence is simple it’s about data integrity and avoiding overwrites when multiple clients modify the same resource simultaneously.
Explaining It in Plain English
Imagine this scenario:
You’re editing a document in Google Docs with your teammates. You open the document, make some edits, and click Save but meanwhile, your teammate also made changes and saved their version before you.
Now, without version control, your changes would overwrite theirs. That’s exactly what the 428 Precondition Required status code helps prevent in APIs.
It tells clients:
“Before you modify this resource, prove to me that you’re working on the latest version.”
Why Was 428 Introduced?
In RESTful APIs and general HTTP operations, clients might read a resource, make some modifications locally, and then send an update request. However, if the resource changed in between, blindly applying the update risks overwriting newer changes.
By requiring clients to specify preconditions, servers ensure:
- The client only updates if it's working with the latest version.
- Conflicting requests are detected and avoided.
- Data integrity is preserved.
This is critical for APIs that support concurrent operations or multiple users.
How It Works: The Conditional Request Flow
Let's walk through a complete example of how 428
helps prevent lost updates in a collaborative editing scenario.
Step 1: User A Fetches the Resource
User A retrieves the current document:
GET /documents/123 HTTP/1.1
The server responds with the document and includes an ETag header—a unique identifier for this specific version of the resource:
HTTP/1.1 200 OKContent-Type: application/jsonETag: "abc123"
{
"id": 123,
"title": "Project Proposal",
"content": "Original content...",
"version": "abc123"
}
Step 2: User B Fetches the Same Resource
At roughly the same time, User B also requests the document and gets the same ETag.
Step 3: User A Attempts an Update (Without Condition)
User A tries to update the document but forgets to include a conditional header:
PUT /documents/123 HTTP/1.1Content-Type: application/json
{
"id": 123,
"title": "Project Proposal",
"content": "User A's updated content...",
"version": "abc123"
}
Step 4: The Server's 428 Response
Because this endpoint is configured to require preconditions, the server responds with:
HTTP/1.1 428 Precondition RequiredContent-Type: application/json
{
"error": "precondition_required",
"message": "This resource requires conditional updates. Please include an If-Match header with the current ETag."
}
Step 5: User A Retries with the Correct Header
User A's application sees the 428
response and automatically retries with the proper conditional header:
PUT /documents/123 HTTP/1.1Content-Type: application/jsonIf-Match: "abc123"
{
"id": 123,
"title": "Project Proposal",
"content": "User A's updated content...",
"version": "abc123"
}
The server processes this conditional request successfully and returns a 200 OK
with a new ETag.
Step 6: User B Attempts Their Update
When User B tries to update with their stale ETag, the server can now reject it with a 412 Precondition Failed
, preventing the lost update.
428 vs. 412 Precondition Failed: Understanding the Difference
This is a crucial distinction in the world of conditional requests:
428 Precondition Required
: "You must include a conditional header to make this request." The server is enforcing that clients use conditional requests. This is about requiring the practice.412 Precondition Failed
: "You included a conditional header, but the condition failed." The client did the right thing by including a condition, but the condition evaluated to false (e.g., the ETag didn't match). This is about a failed condition.
Analogy:
428
: A club bouncer saying, "You must show your ID to enter." (Requiring the practice)412
: The bouncer checking your ID and saying, "This ID is expired, you can't come in." (The specific check failed)
Why the 428 Precondition Required Exists
At first glance, it might seem like a hassle. Why not just let clients update freely?
Well, the 428 status exists for a good reason to prevent data loss and ensure consistency in distributed systems.
Let's explore its purpose in more detail.
1. Preventing Lost Updates
The “lost update” problem happens when multiple clients fetch the same resource and update it independently. Without preconditions, one client’s update might silently overwrite another’s.
428 ensures every modification checks whether the resource has changed since it was fetched preventing silent data loss.
2. Ensuring Data Integrity
By requiring preconditions like If-Match
, the server guarantees that updates are only applied to the correct version of a resource. It’s like putting a safety lock on your data.
3. Promoting Safe Concurrency
In systems where many users interact with shared resources think collaborative editing, API integrations, or RESTful services 428 makes concurrency management more predictable and secure.
4. Encouraging Best Practices
By enforcing conditional requests, the server nudges developers to follow RESTful design best practices such as using ETags, conditional GETs, and version checks.
When to Use 428 Precondition Required
You should consider using 428
in these scenarios:
1. Collaborative Editing Applications
Google Docs-style applications where multiple users might edit the same document simultaneously.
2. High-Contention Resources
Any resource that sees frequent updates from multiple sources, such as:
- Product inventory counts
- Ticket booking systems
- Voting or rating systems
- Configuration settings
3. Sensitive Data Updates
Resources where accidental overwrites could have serious consequences, like financial records or medical data.
4. API Design for Safety
When you want to enforce good client behavior and prevent common concurrency issues.
Real-World Scenarios for 428 Precondition Required
1. Concurrent API Editing
When multiple clients modify the same record simultaneously, 428 ensures updates don’t overwrite each other.
2. Versioned APIs
APIs that evolve over time can enforce preconditions to guarantee clients are using compatible versions.
3. Optimistic Locking Systems
Databases or REST APIs that use ETags for optimistic concurrency control rely on preconditions to detect conflicts.
4. File or Object Storage APIs
Cloud storage systems like S3 use conditional requests heavily 428 would be a natural fit for enforcing such rules.
Testing APIs with Apidog

When dealing with concurrency control, Apidog becomes your secret weapon. Testing conditional request flows requires careful setup and multiple steps. Apidog is perfectly suited for this type of testing.
With Apidog, you can:
1. Create Test Scenarios: Build a complete test flow that:
- First, sends a
GET
request to fetch a resource and capture its ETag header - Then, sends a
PUT
request without conditional headers to verify the server returns428
- Finally, sends a
PUT
request with the captured ETag to verify successful update
2. Automate Header Management: Use Apidog's environment variables to automatically store and reuse ETag values across requests.
3. Simulate Race Conditions: Create test suites that simulate multiple users updating the same resource by sending parallel requests with different ETags.
4. Validate Error Responses: Ensure your 428
responses include helpful error messages that guide clients on what they need to do differently.
5. Test Client Resilience: Verify that your client applications correctly handle 428
responses by retrying with the appropriate conditional headers.
Implementation Best Practices
For Server Developers:
- Be Consistent: If you require preconditions for one method (like
PUT
), require them for all state-changing methods on that resource. - Provide Clear Error Messages: Your
428
responses should clearly explain what headers are required and how to obtain the current resource state. - Use Standard Headers: Stick to standard conditional headers like
If-Match
,If-None-Match
,If-Modified-Since
, andIf-Unmodified-Since
. - Consider Graceful Degradation: For less critical resources, you might log the missing precondition but still process the request.
For Client Developers:
- Always Handle 428 Gracefully: When you receive a
428
, don't treat it as a fatal error. Instead, fetch the current resource state and retry with the appropriate conditional headers. - Cache ETags: Store ETags with your local resource copies so you have them ready for subsequent updates.
- Implement Automatic Retry Logic: Build logic that automatically handles
428
responses by refetching and retrying.
The Bigger Picture: Building Robust APIs
The 428 Precondition Required
status code represents a shift toward more robust, self-documenting APIs. By requiring clients to use conditional requests, you're:
- Preventing Data Loss: Eliminating entire categories of concurrency bugs
- Improving API Safety: Making it harder for clients to accidentally corrupt data
- Enforcing Best Practices: Guiding clients toward proper usage patterns
- Providing Better Diagnostics: Giving clear feedback when clients make mistakes
Conclusion: From Reactive to Proactive Error Handling
The HTTP 428 Precondition Required
status code transforms concurrency control from an optional best practice to an enforceable requirement. It moves error handling from being reactive ("your update conflicted with someone else's") to proactive ("you need to prove you're working with current data before I'll even consider your update").
While it might seem like an extra step, this approach ultimately leads to more reliable applications and happier users who don't lose their work to silent data corruption.
For developers building modern web applications, understanding and implementing 428
is a mark of sophistication in API design. It shows you're thinking not just about what your API does, but how it behaves under real-world conditions with multiple users.
And when you're ready to implement and test these sophisticated concurrency controls, a powerful tool like Apidog provides the testing environment you need to ensure your conditional request logic works flawlessly, protecting your users' data and their sanity.