About This Article
This article was created using an automated generation workflow utilizing generative AI. The definitions of 428 in RFC 6585 and If-Match, ETag, and 412 in RFC 9110 were re-verified on October 28, 2026, and conditional branching to prevent concurrent editing overwrite accidents was verified using a Python 3.12.14 state model. Update requests to actual APIs were not executed.Verification Status: 🧪 Official specifications, state model verification, and 5-role self-QA within the same execution completed (external API/WordPress unverified)
Information Verification Date: October 28, 2026 (JST)
Differences Between HTTP 428 and 412: Preventing Overwrites During Concurrent Updates Using If-Match
In an API where two people edit the same document, the content saved by the person who saves later can sometimes erase the changes made by the previous person. By using HTTP If-Match, you can attach a condition to the request stating, "Update only if it remains the exact version I read." If the server requires conditional updates but no condition is attached, it returns 428 Precondition Required, and if the attached condition does not match the current state, it typically returns 412 Precondition Failed.
This difference is not simply a matter of memorizing error codes. It is important to understand the collaborative relationship designed to prevent data loss, where the client records "which version it read" and the server verifies it immediately before updating.
- First, Consider a Scenario Where Updates Are Lost
- Receiving an ETag via GET and returning it via If-Match
- 428 means "Precondition Required", 412 means "Precondition Failed"
- What matters in If-Match is "strong comparison"
- Do not resend the same PUT upon receiving a 412
- What is the difference from 409 and 304?
- Checklist when designing APIs for production
- Consider changing one place
- Boundary values verified in the state model
- Conclusion
- Official and Primary Sources
First, Consider a Scenario Where Updates Are Lost
Suppose a fictional document API stores the contents of a shared memo as an "initial draft." Person A and Person B perform a GET request almost simultaneously and receive the same content. After Person A saves "A's modification," if Person B performs a PUT based on the old "initial draft" as "B's modification," a design that unconditionally overwrites could cause Person A's changes to be lost. This is a lost update.
For example, in an API that completely replaces the body of a document using a PUT request, if Person B sends editing results based on old content without knowing about Person A's changes, it tends to result in a last-write-wins outcome. HTTP itself does not automatically prevent conflicts for all APIs. The API must implement appropriate conditional updates, and the client must also send those conditions.
sequenceDiagram
participant A as 編集者A
participant B as 編集者B
participant S as 文書API
A->>S: GET /documents/42
S-->>A: 200 + ETag "v7"
B->>S: GET /documents/42
S-->>B: 200 + ETag "v7"
A->>S: PUT + If-Match "v7"
S-->>A: 200 + 新ETag "v8"
B->>S: PUT + If-Match "v7"
S-->>B: 412 Precondition Failed
Note over B,S: Bの古い版による上書きを防止
The ETag string in the diagram is a fictitious value for illustration purposes. It does not imply that actual ETags will follow sequential numbers like v7 or v8. The server generates identifiers based on internal version control, representation content, etc., and the client does not need to deduce the value generation rules.
Receiving an ETag via GET and returning it via If-Match
ETag is an HTTP field used by the server to identify the version of a selected resource representation. When an ETag is provided in a GET response, the client can retain that value. An ETag is neither "encrypted data" nor a "token for user authentication"; it is a validator used to compare whether contents are identical.
Consider the following fictitious initial GET response.
HTTP/1.1 200 OK
Content-Type: application/json
ETag: "v7"
{"title":"共有メモ","text":"初期案"}
After editing, user A specifies the ETag received via GET directly in the If-Match header. The following is an example showing the communication format and is not an executed API log.
PUT /documents/42 HTTP/1.1
Host: example.com
Content-Type: application/json
If-Match: "v7"
{"title":"共有メモ","text":"Aの修正"}
The server compares the current representation with the If-Match condition immediately before applying the update process. If the current version matches, the update can proceed. If the version has changed to v8 after user A's update, user B's update specifying the old v7 will result in a precondition failure.
The important point is that the client performing a GET beforehand does not in itself guarantee safetyAnother update may occur between the GET and the PUT. It only becomes meaningful when the server handles the condition check and the actual modification in a race-condition-free manner. If the application allows a conflict where "another process overwrites the data immediately after verification," the purpose cannot be achieved even when using If-Match.
The evaluation order of these preconditions is defined in Conditional Requests in RFC 9110.
428 means "Precondition Required", 412 means "Precondition Failed"
Even with similar status codes, the cause and the user's next action differ.
| Response | Main Meaning | Next Action |
|---|---|---|
| 428 Precondition Required | The server requires a conditional request. | Retrieve the latest version and attach the required conditions. |
| 412 Precondition Failed | The specified precondition evaluated to false. | Compare your changes with the latest version. |
| 409 Conflict | The request conflicts with the current state of the resource. | Check the conflict reason and resolution steps returned by the API. |
If you forgot to include conditions: 428
If the server policy states "If-Match must always be specified when updating this document," unconditional PUT requests can be rejected.
PUT /documents/42 HTTP/1.1
Host: example.com
Content-Type: application/json
{"title":"共有メモ","text":"変更案"}
HTTP/1.1 428 Precondition Required
Content-Type: application/json
{"error":"If-Match is required"}
This response does not mean that the ETag is outdated.It represents the state where conditional processing is expected, but the required conditions are missing. RFC 6585 recommends that clients receiving a 428 status code should be instructed on how to retransmit the request. It also specifies that 428 responses must not be stored in caches.
However, the use of 428 is not mandatory for all servers. If the API permits unconditional updates, it may accept the same request. It is incorrect to assume that conflict prevention is implemented simply because a 428 status is not returned.
Reference:RFC 6585 §3 — 428 Precondition Required。
If you provided outdated conditions: 412
If user B issues a PUT request with v7 as the condition after user A's update has advanced the current ETag to v8, it will not match the current representation.
PUT /documents/42 HTTP/1.1
Host: example.com
Content-Type: application/json
If-Match: "v7"
{"title":"共有メモ","text":"Bの修正"}
HTTP/1.1 412 Precondition Failed
412 indicates that at least one of the conditions evaluated by the server was false. In typical conflict scenarios, the server does not apply the update and requires the client to check the current version. However, RFC 9110 also provides exceptions for cases where a state-changing request can be determined to have already succeeded, soAvoid assuming that any response other than 412 always means a new update was executed.
Reference:RFC 9110 §13.1.1 — If-Match、§15.5.13 — 412。
What matters in If-Match is "strong comparison"
In addition to normal quoted tags, ETags can include weak ETags prefixed with W/. Weak ETags are used to indicate that representations are semantically equivalent, and they are distinct from use cases requiring byte-for-byte identity.
If-Match evaluation uses strong comparison. Therefore, the following two do not match under If-Match.
ETag: "v7" If-Match: W/"v7"
Even if the visual identifier strings are the same, a tag with W/ cannot be treated as identical to a strong ETag. If you use If-Match for API update control, you must verify the strength of the ETag returned by GET and the server's comparison implementation. When a 412 occurs because a weak ETag was passed to If-Match, do not simply strip the quotes or arbitrarily remove the W/. Instead, re-fetch the current representation and use the correct validator specified by the API.
Additionally, If-Match: * indicates that a current representation exists, but does not require it to match a specific version. Existence checks and concurrent editing version conflict detection are separate issues. You should not recklessly replace it with an asterisk for the purpose of preventing lost updates.
Reference:RFC 9110 §8.8.3 — ETag、§13.1.1 — If-Match。
Do not resend the same PUT upon receiving a 412
The risk when a 412 status occurs is updating only the conditions to the latest ETag without resolving the conflict and resending the same stale body. This risks unintentionally overwriting User A's changes, which you originally wanted to protect.
In a secure workflow, you first retrieve the latest version and its ETag using GET. Next, you check for differences from your own edits, and if there are conflicting changes, you either prompt a user to choose or use the merge logic defined by the API. Once the merged content is finalized, you perform the update using the retrieved latest version's ETag. Even after receiving a successful update response, you save the new version information returned by the API and read it back for verification if necessary.
For example, if User A changed the approver and User B changed the description, both edits might be preserved. On the other hand, if both changed the exact same approver field to different values, automatically choosing one is risky. A 412 status is an entry point for detecting conflicts and is not a mechanism that determines the correct merging policy.
What is the difference from 409 and 304?
409 Conflict is a more general response when a request cannot be completed due to a conflict with the current state of the resource. It is used for reasons other than an ETag mismatch, such as when a state transition is prohibited by business rules. Which status to return depends on API design, but 412 corresponds specifically to the case where the precondition of If-Match is false.
304 Not Modified is a response mainly used in cache revalidation. For example, be careful not to confuse the meaning of using If-None-Match with GET and using If-Match with PUT. Even when using a common validator like an ETag, the purpose changes depending on the combination of header and method.
Memorizing rules such as "always 304 because an ETag exists" or "428 because the ETag is old" will not help you troubleshoot incidents.Which HTTP method was used and which conditional header was sent is the shortcut to check first.
Checklist when designing APIs for production
For update APIs such as shared documents or configuration management, explicitly state in the API contract whether PUT requests without conditions are permitted or rejected with 428. If conditional updates are adopted, a possible design returns an available ETag in the GET response, validates If-Match during the PUT request, and returns the next version information once the update succeeds. It is also necessary to confirm that the update and comparison are performed atomically and that database update conflicts are not missed.
During incident investigation, organizing records in the following order will reveal the cause.
Which ETag was received in the GET request before the update.
Whether If-Match was included in the update request.
Whether the sent value was a strong ETag or a weak ETag.
Whether the response was 428, 412, or 409.
Whether another update existed at the same time.
Whether the stale body was automatically re-sent after the update failed.
There is no need to unconditionally record massive amounts of ETag or document content to operational logs. For APIs containing sensitive information, the policy is to retain only the minimum necessary information, such as correlation IDs, statuses, and anonymized version identifiers, while keeping authentication information and document bodies out of the logs. ETags are not a substitute for authentication and authorization, and access control checks are still required separately.
Furthermore, HTTP conditional updates do not guarantee the consistency of an entire business transaction spanning multiple resources. Even if a single document can be updated safely, interdependencies with other documents, duplicate notifications, and side effects on external systems remain design challenges for the application side.
Consider changing one place
In the first example, if Person B's If-Match header is changed from v7 to v8, the condition will match the current version. However, you must not change only the condition without re-editing Person B's body text. This is a thought experiment to understand the meaning of the conditions and is not a recommended retry procedure.
Another consideration is what happens if the If-Match header itself is removed. For an API that requires conditional updates, a 428 status is expected, whereas an API that allows unconditional updates might succeed in overwriting. In other words, 428 represents the server policy of "requiring a condition," while 412 represents a different stage, which is "the evaluation result of the sent condition."
Boundary values verified in the state model
On October 28, 2026, the branch logic in the text was re-verified using an in-memory state model in Python 3.12.14. This was not a communication test against an actual service, but a verification isolating only the ETag and If-Match conditions.
| Condition | Expected Result | Observed |
|---|---|---|
| Conditional update required, but missing If-Match | 428, body remains unchanged | Match |
Current strong ETag "v7" | 200, body updated | Match |
Reuse old "v7" after update | 412, keep prior update | Match |
Weak ETagW/"v8" | 412, body unchanged | Match |
Re-fetch the latest version and update with "v8" update at | 200, update to merged body | Match |
| Server does not require conditions | May accept unconditional update | Match |
All 6 cases behaved as expected. However, this does not verify HTTP servers, database atomic updates, proxies, or actual API ETag generation rules. The success of the model cannot be used as a guarantee of implementation for the target system.
Conclusion
HTTP 428 indicates that a conditional request is required, and 412 indicates that the sent condition was not met. By using If-Match and strong ETags, you can design updates to occur only when the read version has not changed. However, this does not automatically resolve post-conflict merging, authorization, or multi-resource consistency.
When an update conflicts, rather than removing the condition and resending, fetch the latest version and check for differences. This extra step connects HTTP status codes to actual data protection.
Official and Primary Sources
Note: Communication examples are constructed for illustrative purposes only. Apart from Python state model verification, update testing against live services, response collection, and WordPress rendering checks are not performed.
