REST API Conditional Requests

RESTful Web Services Tutorials


REST API conditional requests let a client ask the server to send or change a resource only when a stated condition is true. They reduce unnecessary response bodies, improve cache validation, and prevent one client from silently overwriting another client's newer update.

The server supplies a validator such as an ETag or Last-Modified value. The client returns that validator in a conditional header on its next request. The server then compares the value with the current representation before choosing a response.

Understand Conditional Request Headers

Header Role Typical result
ETag Identifies a specific representation version. Sent with a successful response.
Last-Modified States when the resource last changed. Sent with a successful response.
If-None-Match Continues only when no listed tag matches. 304 for cached GET requests.
If-Match Continues only when a listed strong tag matches. 412 when an update is stale.
If-Modified-Since Checks whether a resource changed after a date. 304 when it did not change.
If-Unmodified-Since Continues only when a resource has not changed after a date. 412 when it changed.
If-Range Requests a range only while a validator still matches. 206 or a complete 200 response.

Validate a Cached GET with an ETag

On the first request, the API returns the representation and a quoted entity tag. Treat the tag as an opaque server value; the client does not need to understand how the server generated it.

Example:

GET /api/articles/42 HTTP/1.1
Host: api.example.com
Accept: application/json

# The first request has no cached validator.

Response:

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: private, max-age=0, must-revalidate
ETag: "article-42-v7"

{"id":42,"title":"Conditional Requests","version":7}

The client stores both the response and the ETag. When it needs the resource again, it sends the tag through If-None-Match.

Example:

GET /api/articles/42 HTTP/1.1
Host: api.example.com
Accept: application/json
If-None-Match: "article-42-v7"

# Ask for a body only when the representation changed.

If the current tag still matches, the server returns 304 Not Modified without a message body. The client reuses its stored representation.

Response:

HTTP/1.1 304 Not Modified
ETag: "article-42-v7"
Cache-Control: private, max-age=0, must-revalidate

# A 304 response does not contain the resource body.

When a request contains both If-None-Match and If-Modified-Since, HTTP gives If-None-Match precedence. Prefer ETags when you can generate them reliably.

Prevent Lost Updates with If-Match

Conditional requests also protect writes. Suppose Priya and Oliver read version 7 of the same article. Priya saves first, so the server moves the resource to version 8. Oliver must not overwrite Priya's work using the stale version 7 data.

Send the previously received strong ETag in If-Match with the update request.

Example:

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

{"title":"Safer REST API Updates"}

Because the current server tag is now version 8, the condition fails. The server returns 412 Precondition Failed and leaves the resource unchanged.

Response:

HTTP/1.1 412 Precondition Failed
Content-Type: application/problem+json
ETag: "article-42-v8"

{"title":"Stale resource version","status":412}

The client can fetch the latest representation, show the conflict to the user, merge changes where appropriate, and retry with the new ETag.

Create a Resource Only When It Is Missing

Use If-None-Match: * when a PUT request should create a resource only if no current representation exists. This atomic check avoids a separate check-then-create race.

Example:

PUT /api/usernames/anaya HTTP/1.1
Host: api.example.com
Content-Type: application/json
If-None-Match: *

{"displayName":"Anaya Shah"}

The server creates the resource when the target is missing. If it already exists, the server responds with 412 Precondition Failed instead of replacing it.

Choose Strong or Weak ETags

A strong ETag identifies a byte-for-byte equivalent representation and appears as a quoted value such as "v8". A weak tag begins with W/ and indicates semantic equivalence even when the bytes are not identical.

  • Use strong ETags for If-Match, write protection, and range requests.
  • Use weak ETags for cache validation when minor representation differences do not matter.
  • Generate a different validator whenever the selected representation changes.
  • Keep quotation marks because entity-tag syntax requires them.

Example:

ETag: "invoice-91-v3"
ETag: W/"news-feed-2026-09-01"

# The W/ prefix marks the second validator as weak.

Use Last-Modified When Dates Are Sufficient

Last-Modified and date-based conditions are convenient when your data already has a reliable update timestamp. However, HTTP dates have one-second precision and may not distinguish rapid changes. ETags can represent versions more precisely.

Example:

GET /api/guides/15 HTTP/1.1
Host: api.example.com
If-Modified-Since: Tue, 01 Sep 2026 08:30:00 GMT

# The server returns 304 when the guide has not changed.

Implement Conditional Requests Correctly

  1. Generate a validator from the selected representation or its version.
  2. Return ETag or Last-Modified with successful GET and HEAD responses.
  3. Evaluate request preconditions before running the method when HTTP requires it.
  4. Return 304 for a matching cache-validation GET or HEAD request.
  5. Return 412 when a write precondition fails.
  6. Send a new validator after every successful change.

Example:

// Create a strong ETag from the stored version number.
const etag = `"article-${article.id}-v${article.version}"`;

// Reject a stale update before changing the database.
if (request.headers["if-match"] !== etag) {
  response.status(412).set("ETag", etag).end();
  return;
}

// Apply the update and return the validator for the new version.
const updated = await updateArticle(article, request.body);
response.set("ETag", `"article-${updated.id}-v${updated.version}"`);
response.json(updated);

This simplified example accepts one exact tag. Production code must parse header syntax correctly, handle lists and the wildcard, follow method-specific precedence rules, and evaluate strong or weak comparisons as defined by HTTP.

Conditional Request Best Practices

  • Evaluate validators for the exact representation, including content encoding and negotiated format.
  • Combine validators with suitable Cache-Control and Vary headers.
  • Do not use predictable user-specific ETags as a tracking mechanism.
  • Return no representation body with a 304 response.
  • Use 412 for a failed precondition, not as a general validation error.
  • Test concurrent updates so stale clients cannot replace newer data.

Conclusion

REST API conditional requests make reads more efficient and writes safer. Use ETag with If-None-Match to validate cached responses, and use a strong ETag with If-Match to reject stale updates. Add If-None-Match with a wildcard when creation must not overwrite an existing resource. Correct validators and status codes give clients a clear, standards-based way to handle caching and concurrency.



Found This Page Useful? Share It!
Get the Latest Tutorials and Updates
Join us on Telegram