HTTP Conditional Request Interview Questions: ETags, 304s, and Lost Updates

Practice HTTP conditional request interviews with ETags, 304 responses, If-Match concurrency control, weak validators, and a worked lost-update recovery case.

Author: PracHub

Published: 9/22/2026

HTTP Conditional Request Interview Questions: ETags, 304s, and Lost Updates

September 22, 2026

Quick Overview

Cache revalidation and lost-update prevention use validators for different decisions; correct concurrency depends on an atomic comparison and mutation.

Backend EngineerFree

Two people open the same document. One changes the title; the other changes the owner. Both save successfully, but the second save silently restores the old title. The API returned successful responses while losing a valid edit.

HTTP conditional requests solve two related problems: avoiding an unnecessary representation transfer and refusing an update based on stale state. In an interview, explain which decision the validator controls, then show where the comparison becomes atomic with the write. An ETag header alone does not prevent lost updates.

Use PracHub's Backend Engineer questions to practice the implementation boundary. Evidence boundary: protocol claims below come from official HTTP standards and MDN documentation. The document scenario and answer recommendations are original exercises; no verified candidate reports establish a particular employer's use of these questions.

Cache revalidation with If-None-Match compared with concurrent updates using If-Match

What does an ETag identify?

Documented fact: an entity tag is an opaque validator for a selected representation. It need not be a content hash. A server can generate it from a revision identifier or another suitable representation-specific value. A weak tag carries the W/ prefix; it does not promise byte-for-byte identity. See MDN's ETag reference.

For this exercise, "doc-42-v7" identifies one JSON representation of document 42. Treat the quotes as part of the HTTP syntax. The client stores and returns the tag; it should not parse “v7,” increment it, or infer business meaning from its spelling.

A revision counter is safe only if its generation contract matches the representation. If the response incorporates permissions, a computed field, or another table, changing those inputs may also need to change the validator. Otherwise, a server can incorrectly validate bytes that no longer represent the current response.

That last point is an implementation inference: the validator must track what your endpoint actually returns. Naming a database column version does not establish that property.

When should the server return 304 instead of 200?

A client holding an earlier response can send:

GET /documents/42 HTTP/1.1
Host: api.example.test
If-None-Match: "doc-42-v7"

Documented fact: If-None-Match uses weak comparison. If the current representation matches a supplied tag, a conditional GET or HEAD normally receives 304 Not Modified. A matching condition on another method results in 412 Precondition Failed. If the condition permits the request, normal method processing continues. See the If-None-Match reference.

The names can sound backward. “None match” is the condition for proceeding with the ordinary operation. When a current tag matches, that condition is false. For a retrieval, the server can tell the cache to reuse its stored representation.

Documented fact: a 304 response has no message body. It carries applicable metadata so the stored response can be validated. It is not an empty replacement document. See MDN's 304 reference.

An interview follow-up might ask why the UI became blank after a 304. Inspect whether application code replaced the cached body with the empty network response. The correct behavior depends on which layer owns caching: a browser cache, an SDK, or your own application cache. Do not assume every client library exposes revalidation in the same way.

How does If-Match prevent a lost update?

Both editors read document 42 at version 7. Editor A submits a replacement with If-Match: "doc-42-v7". The server checks that version and commits version 8. Editor B then submits a replacement based on version 7 with the same old tag.

Documented fact: If-Match uses strong comparison. A weak validator cannot satisfy it. A failed precondition ordinarily produces 412 rather than performing the requested mutation. The wildcard checks whether a current representation exists; it does not pin a specific revision. See MDN's If-Match reference.

For the two-editor case, reject B's stale update and preserve A's work. B should retrieve the current representation, reconcile the intended change, and submit against the new validator. Blindly replacing the old tag with the newest one and replaying the same full document can still overwrite the other editor’s change.

This protection is useful for PATCH too. A partial update may still depend on an old balance, workflow state, or relationship between fields. Whether two edits commute is a business-rule question, not something the HTTP verb decides automatically.

Two editors read version seven; the first commits version eight and the second receives 412

Where must the version check happen?

Original implementation exercise: assume one document row owns the complete representation, authorization has already succeeded, and the server has validated a single strong tag. Translate that tag into a trusted expected version, then combine comparison and mutation:

UPDATE documents
SET body = :new_body,
    version = version + 1
WHERE id = :document_id
  AND version = :expected_version;

An affected-row count of one means the compare-and-update succeeded. Zero means this statement did not apply the mutation. Map the outcome according to your endpoint's existence, authorization, and precondition contract; do not leak another user's resource through inconsistent errors.

The unsafe alternative is “SELECT version, compare in application code, then UPDATE by ID.” Two workers can both see version 7, both pass the comparison, and overwrite each other. A transaction alone does not fix this unless its isolation, locking, or conditional mutation prevents that interleaving.

A small SQLite fixture tested the conditional statement with these steps:

  1. Insert document 42 with body original and version 7.
  2. Apply A's body with expected version 7: one row changes, now version 8.
  3. Apply B's body with expected version 7: zero rows change; A's body remains.
  4. Reconcile B's intent and apply with expected version 8: one row changes, now version 9.

Local observation: the fixture produced application outcomes 204, 412, and 204, with final body merged edit at version 9. It verifies the database comparison, not a full HTTP server or every database's concurrency behavior. The HTTP adapter still needs parsing, authorization, transaction error handling, and correct response headers.

If the representation spans multiple rows, identify its concurrency boundary. You might lock an aggregate row and update its revision alongside dependent rows, or use a transaction with appropriately enforced invariants. Explain which competing update must fail and why the chosen mechanism detects it.

Interview cases that separate the headers

Use these compact cases to check your explanation. Assume an authorized request, a valid endpoint, no other failing conditions, and the exercise's single representation.

  • Current tag "v7"; GET with If-None-Match: W/"v7". Weak comparison can validate the cached representation, so the retrieval returns 304.
  • Current tag "v7"; PUT with If-Match: W/"v7". Weak comparison is insufficient for this condition; the update receives 412.
  • Current tag "v8"; PUT with If-Match: "v7". The stale revision fails. Do not overwrite version 8.
  • A representation exists; PUT with If-None-Match: *. The create-only precondition fails. If no current representation exists and other checks pass, creation can proceed.
  • A representation exists; PUT with If-Match: *. Existence satisfies this condition, but it does not prove the caller edited the latest revision.

The final two cases require atomic enforcement just like the versioned update. For create-only behavior, a unique constraint or equivalent conditional creation must close the race between checking absence and inserting.

Standards detail: preconditions have a defined evaluation order. Entity-tag conditions take precedence over their corresponding date-based alternatives; an implementation should not combine all headers as independent ad hoc checks. RFC 9110 also permits a successful response in certain failed-precondition retry cases when the server can verify that the requested state change already succeeded. That is narrower than treating every stale retry as success. See RFC 9110, conditional requests.

What if the successful response is lost?

Suppose A's update commits, but its response never reaches the client. Retrying with version 7 can now encounter version 8. A 412 does not by itself distinguish “someone else changed the document” from “my previous request succeeded.”

Editorial design guidance: keep concurrency control and operation identity separate. An ETag describes the state the caller observed. An idempotency key identifies a particular business operation. Where duplicate execution matters, persist the operation identity, request fingerprint, and outcome under the same appropriate transactional boundary as the effect.

A recovery response should give the client a way to reconcile: retrieve current state, look up the original operation, or compare a server-recorded result. Do not automatically retry with a newly fetched validator when the user's intent may conflict with intervening edits.

Also distinguish a transport retry from a new user action. If the user changes the requested content, reusing the previous operation identity can be incorrect. Define whether the API rejects a reused key with different input and how long it retains the original result.

The broader REST API interview guide covers idempotency and method semantics. Here, the key interview distinction is that validators and operation IDs answer different questions.

How do representation variants change the answer?

Imagine the same URL returns English or French content, or different encodings. A cache needs enough request context to select the correct stored response, and validators must be valid for the selected representation. A matching tag from the wrong variant does not justify reusing an unrelated body.

Documented guidance: HTTP conditional requests work with the representation selected for the request. MDN's conditional requests guide discusses validators and the distinction between weak and strong validation.

Design inference: review cache selection and validation together. Check relevant Vary behavior, authorization boundaries, transformation behavior, and which component generates or rewrites the ETag. A shared cache serving one tenant's response to another is not repaired merely by making the tags stronger.

Build regression tests around changed inputs: edit the body, change a dependent field, switch a negotiated variant, delete and recreate the resource, and retry after a lost response. Assert both the response code and the durable state. Check the final stored document as well as the headers; otherwise, the test can pass while an edit is lost.

Add a deterministic race test for the unsafe implementation. Pause both workers after they read version 7, then let each perform its unconditional update. The final state should expose the overwritten edit. Replace the mutation with the conditional statement and run the same schedule: only one update based on version 7 should succeed. This demonstrates why the fix works rather than relying on a test that rarely happens to collide.

At the HTTP boundary, keep malformed syntax separate from a valid but stale precondition. Test a missing validator according to the endpoint's documented policy, a properly quoted stale tag, a weak tag on a guarded mutation, and a list of valid tags if the implementation supports the standard field grammar. The small local model checked seven already-parsed single-validator cases; it does not justify implementing header parsing with a casual string split.

Practice conditional updates and cache behavior

These question-bank records provide adjacent practice, not predictions of an exact interview. For each, identify the state comparison and the component that enforces it.

PracHub practice questionWhat to rehearse
Implement an Idempotent Versioned Database UpdateMake version comparison atomic with the mutation.
Trace and reduce redundant curl requestsDistinguish repeated transfer from cache revalidation.
Harden a URL-Caching HTTP Service for ProductionDefine cache ownership, validators and failure handling.
Extend an Existing Endpoint Cache and Prove It with TestsTest changed input and stale cached output.
Explain Python Concurrency, HTTP Update Semantics, Garbage Collection, and DeadlocksConnect update semantics to concurrent execution.

Choose one Backend Engineer practice question and trace two competing clients through it. Explain what each sees after a stale update, a successful retry, and a missing response before discussing performance optimizations.

Sources and Further Reading

Checked September 21, 2026. Example tags, documents, and status mappings are teaching examples. Validate framework behavior against the HTTP standard and your endpoint's documented contract.


Comments (0)