Two API editors sending updates to one resource while a version gate accepts the current change and routes a stale change to refresh and merge
A strong ETag turns a stale write into a visible conflict instead of a silent overwrite.

Two people open the same record. One saves a new address. The other saves an older copy a minute later, and the first change disappears.

The second request may be valid JSON from an authorized user. It is still based on stale state. Authentication cannot catch that conflict.

HTTP already has a guard for this case. Give each representation a strong entity tag, then require clients to send that tag back with If-Match.

Use an ETag as an opaque version

An ETag identifies a selected representation. The value is opaque to the client, so do not make callers parse a database counter or timestamp from it.

A strong validator changes whenever the representation data changes in a way that matters for exact comparison. A weak ETag starts with W/. It can help a cache decide that two responses are equivalent, but RFC 9110 uses strong comparison for If-Match.

That difference is the release gate. A weak tag will not give you the exact-match rule needed to stop a lost update.

GoalRequest conditionFailure result
Update only the version readIf-Match: "v17"412 when the current tag differs
Delete only the version readIf-Match: "v17"412 when another change won
Create only when absentIf-None-Match: *412 when the resource exists
Refresh a cached GETIf-None-Match: "v17"304 when it is unchanged

Make the comparison part of the write

The safe flow begins with a read:

GET /orders/42

200 OK
ETag: "v17"

The client keeps the body and the tag. When it edits that version, it sends both:

PATCH /orders/42
If-Match: "v17"
Content-Type: application/json

{"shipping_city":"Karachi"}

The origin must compare "v17" with the current version and apply the change as one atomic operation. If the stored version is now "v18", it must not apply this body. A 412 Precondition Failed makes the conflict visible.

Do not read the version, release a database lock, and update later. Another writer can enter that gap. Use a conditional update, transaction or compare-and-swap operation near the system of record.

Treat 412 as a merge decision

A stale request is not a transient network error. Retrying the same body with the new ETag can erase the change that caused the conflict.

Fetch the current representation. Show the fields that changed, then let a person merge them or apply a product-specific rule. A background client can stop and return a conflict object when it cannot make that choice safely.

This differs from webhook idempotency. An idempotency key stops the same delivery from performing an action twice. If-Match stops a write that was prepared from an older state. Some operations need both controls.

Require conditions where silent writes are unsafe

An API can reject an unsafe request that lacks a required condition. RFC 6585 defines 428 Precondition Required for this policy.

Use it on resources where an overwrite would be costly: billing settings, routing rules, access policy, inventory or a shared document. Explain which validator the client must fetch and send.

Check every writer first. Mobile clients, scheduled jobs and old browser tabs may omit the header. The service-worker update guide shows why stale clients need a survival plan during a protocol change.

If-Match: * asks whether any current representation exists. It does not prove that the caller read the latest version, so it is not a replacement for a specific tag in an edit flow.

Generate validators from the saved representation

The tag must follow the representation a client is editing. Content coding, language variants and server transformations can produce different representations of one resource.

RFC 9110 places a strict caveat on a successful PUT: a server must not send a validator unless it saved the received representation without transforming it and the validator describes that new representation. If your write pipeline normalizes input, return the updated representation through a documented response or require a fresh GET.

Do not use a low-resolution modification time when two updates can happen inside the same tick. A database version column, revision identifier or strong digest can back the ETag, but clients should still treat the result as opaque.

Test the conflict path before enforcing it

  1. Read one resource twice. Keep both copies and their matching ETag.
  2. Update copy A. Confirm the response exposes the new state or a way to fetch it.
  3. Submit copy B with the old tag. Expect 412 and no stored change.
  4. Test a missing condition. Expect 428 only where the API promises to require one.
  5. Trace every writer. Confirm batch jobs, SDKs and administrative tools preserve the condition.

These are procedures for your team to run. TechWeb did not test your API, database, proxy or client. For a purchased platform, add conditional-write behavior to the vendor evidence review.

Frequently asked questions

Is an ETag only for browser caching?

No. Strong entity tags also support conditional state-changing requests that prevent lost updates.

Can I use a weak ETag with If-Match?

Not for this purpose. If-Match uses strong comparison, so a weak tag does not match a strong current validator.

Should a client retry after 412?

It should first fetch the current state and merge or stop. Blindly replaying the stale body can recreate the overwrite.

Does If-Match replace a database transaction?

No. The origin still needs an atomic comparison and write. It also needs normal authorization and validation.

Sources

IETF RFC 9110: HTTP Semantics · IETF RFC 6585: Additional HTTP Status Codes · MDN: ETag · MDN: If-Match. Framework behavior varies; verify the origin and storage path before enforcement.

More in this sectionTechnology →