Skip to main content

If-Match

RequestActive

If-Match makes a request conditional on the resource's current ETag matching the value the client last saw. Used for optimistic concurrency: a PUT or PATCH only applies if no other client has modified the resource since you read it. Mismatch → 412 Precondition Failed. If-Match: * succeeds only if the resource exists (create-only-if-absent: use If-None-Match: * instead).

If-Match: "<etag>" | * If-Match: "<etag1>", "<etag2>"

Description

If-Match implements optimistic concurrency control in HTTP without pessimistic locking. Race condition timeline (why If-Match exists): T=0 Client A: GET /articles/42 → ETag: "v7" T=0 Client B: GET /articles/42 → ETag: "v7" T=1 Client B: PUT /articles/42 If-Match: "v7" → 200 OK, ETag now "v8" T=2 Client A: PUT /articles/42 If-Match: "v7" → 412 Precondition Failed Client A must re-fetch (new ETag "v8"), merge, and retry with If-Match: "v8". Without If-Match, Client A's write at T=2 would silently overwrite Client B's work. Workflow: 1. Read resource: GET /resource → server returns ETag in response header 2. Edit locally 3. Write with guard: PUT/PATCH/DELETE + If-Match: "<etag>" 4a. ETag matches → server applies change → 200/204, new ETag 4b. ETag changed → 412 Precondition Failed → re-fetch, merge, retry If-Match: * semantics: Matches any current representation – but only if the resource EXISTS. Does NOT match if the resource does not exist (returns 412). To create-only-if-absent, use If-None-Match: * (412 if resource already exists). Strong vs weak ETags: If-Match requires STRONG ETags. Weak ETags (W/"abc") MUST NOT be used with If-Match for unsafe methods (PUT, PATCH, DELETE) per RFC 9110 §13.1.1. A server receiving a weak ETag in If-Match for an unsafe method SHOULD return 412. Semantic relationships: ETag ←generated by server, identifies resource state ↓ sent by client in If-Match → unsafe request (PUT/PATCH/DELETE) → success or 412 If-None-Match → safe request (GET/HEAD) → 304 or full response ↓ 412 Precondition Failed → client re-fetches with GET, gets new ETag ↓ Client merges and retries with new ETag → 200/204

Directives

DirectiveDescription
"<etag>"Apply the operation only if the resource's current ETag exactly matches this strong ETag value. Multiple ETags (comma-separated) succeed if ANY match.
*Apply the operation regardless of current ETag value – but ONLY if the resource exists. Returns 412 if the resource does not exist at all.
"etag1", "etag2"Multiple ETags: the operation proceeds if the current ETag matches any of the listed values. Useful when accepting multiple known versions.

Examples

Conditional PUT – optimistic update
http
GET /api/articles/42 HTTP/1.1
Host: api.example.com

HTTP/1.1 200 OK
ETag: "v7-abc123"
Content-Type: application/json

{"title": "Original Title", "body": "..."}

---

PUT /api/articles/42 HTTP/1.1
Host: api.example.com
If-Match: "v7-abc123"
Content-Type: application/json

{"title": "Updated Title", "body": "..."}
412 when another client modified first
http
PUT /api/articles/42 HTTP/1.1
If-Match: "v7-abc123"
Content-Type: application/json

{"title": "My Update"}

HTTP/1.1 412 Precondition Failed
ETag: "v8-xyz789"
Content-Type: application/json

{"error": "precondition_failed", "current_etag": "v8-xyz789"}
Conditional DELETE (guard against double-delete)
http
DELETE /api/sessions/sess_abc HTTP/1.1
If-Match: "active-v1"

HTTP/1.1 204 No Content

# Second attempt after deletion:
DELETE /api/sessions/sess_abc HTTP/1.1
If-Match: "active-v1"

HTTP/1.1 412 Precondition Failed
If-Match: * – update only if resource exists
http
PUT /api/config/limits HTTP/1.1
If-Match: *
Content-Type: application/json

{"max_uploads": 100}

# Returns 412 if /api/config/limits does not exist
# Use If-None-Match: * to create only if NOT exists

Related

Specification

RFC 9110If-Match specification →