412 Precondition Failed
Client Error - Conditional request precondition not met
HTTP 412 Precondition Failed
What It Means
The HTTP 412 Precondition Failed status code indicates that one or more conditions given in the request headers (like If-Match or If-Unmodified-Since) evaluated to false on the server.
Common Precondition Headers
| Header | Purpose |
|---|---|
| If-Match | Only proceed if ETag matches (optimistic locking) |
| If-Unmodified-Since | Only proceed if not modified since date |
| If-None-Match | Used for caching (returns 304 if match) |
Example: Optimistic Locking
# Client has ETag "abc123" from previous GET
PUT /api/documents/1 HTTP/1.1
If-Match: "abc123"
Content-Type: application/json
{"title": "Updated Title"}
---
# If document was modified by someone else (ETag changed):
HTTP/1.1 412 Precondition Failed
Content-Type: application/json
{
"error": "Precondition Failed",
"message": "Document was modified. Your ETag: abc123, Current: def456"
}
412 vs 409
- 412: Header-based precondition failed (If-Match, etc.)
- 409: Business logic conflict (duplicate email, etc.)
Implementation
app.put('/api/documents/:id', async (req, res) => {
const ifMatch = req.headers['if-match'];
const doc = await Document.findById(req.params.id);
if (ifMatch && ifMatch !== doc.etag) {
return res.status(412).json({
error: 'Precondition Failed',
message: 'Document was modified by another request',
currentETag: doc.etag
});
}
// Proceed with update
doc.title = req.body.title;
doc.etag = generateNewETag();
await doc.save();
res.set('ETag', doc.etag);
res.json(doc);
});
Use Cases
- Preventing lost updates: Don't overwrite concurrent changes
- Conditional uploads: Only upload if file doesn't exist
- Safe deletes: Only delete if resource is unchanged