ETags and conditional requests

API design: REST, HTTP, GraphQL and gRPC Mid-level Python 3.13+ PostgreSQL 17+ Reviewed By Best Latest Apps

Predict the output

The client sends If-Match with an old ETag. What does the handler return?

Python 3.13
def update(current_version: int, if_match: str) -> int:
    if if_match != f'"v{current_version}"':
        return 412
    return 200

print(update(current_version=8, if_match='"v7"'))

Options
Hint 1 of 1

Which version did the client read?

Choose an answer, then open Show answer below to check it.

Show answer

Answer: A 412

Correct. The resource moved to v8 since the client read v7, so the update is refused.

If-Match turns the update into compare-and-set: it only applies while the resource is still the version the client read. A mismatch means someone else wrote first, and 412 Precondition Failed says so.

Why not the others?
  • B 200. That would overwrite someone else's change.
  • C 304. 304 answers conditional GETs, not updates.
  • D 409. Possible in some APIs, but If-Match failures are defined as 412.

30-second answer

An ETag is a version identifier for a representation. For caching, the client sends If-None-Match: <etag> and the server answers 304 Not Modified with no body if nothing changed, saving bandwidth. For safe updates, the client sends If-Match: <etag> with a PUT or PATCH; if someone else changed the resource since, the server answers 412 Precondition Failed instead of overwriting. That's optimistic locking over HTTP.

Practise saying it · 30 s. No recording, just a timer

Explain ETags.

Say your answer out loud, then check which of these points you covered:

  • Version of a representation
  • If-None-Match → 304
  • If-Match → 412
  • Optimistic locking over HTTP

Deep dive

Derive the ETag from a version column or a hash of the representation.

Weak ETags (W/"…") mean semantically equal; use strong ones for If-Match.

Return 428 Precondition Required if you insist clients send If-Match.

Last-Modified/If-Modified-Since is the older, second-granularity version of the same idea.

Common mistakes

  • Computing the ETag by hashing a response that includes a timestamp, so it never matches.
  • Ignoring If-Match and silently overwriting.
  • Returning 304 with a body.

Senior take & follow-ups

Cover both uses: caching with 304 and lost-update protection with 412.

Follow-up questions

Think of your answer first, then open the hint.

  1. Which status for a failed If-Match?

    Show hint

    412 Precondition Failed.

  2. How do ETags relate to a version column?

    Show hint

    The ETag can just be the version; If-Match maps to WHERE version = ?.

Similar on Android: HTTP caching with OkHttp OkHttp's cache sends If-None-Match for you; the server decides the ETag and answers 304.

Suggest a fix if something here is wrong or out of date.