Conditional GET with ETag

A conditional GET: send back the ETag you received earlier via `If-None-Match`, and if the resource is unchanged you get just a 304 Not Modified, saving bandwidth.

Overview

A conditional GET: send back the ETag you received earlier via `If-None-Match`, and if the resource is unchanged you get just a 304 Not Modified, saving bandwidth.

In code

# 1) first request returns an ETag
curl -i https://api.example.com/config
#    ETag: "a1b2c3"

# 2) revalidate: send it back, get 304 if unchanged
curl -i https://api.example.com/config \
  -H 'If-None-Match: "a1b2c3"'
const first = await fetch('/config');
const etag = first.headers.get('ETag');

const next = await fetch('/config', {
  headers: { 'If-None-Match': etag }
});
if (next.status === 304) {
  // not modified — reuse cached body
}
import requests

r1 = requests.get('https://api.example.com/config')
etag = r1.headers['ETag']

r2 = requests.get('https://api.example.com/config',
                  headers={'If-None-Match': etag})
print(r2.status_code)  # 304 if unchanged (r2.content is empty)

Details

Store the first response's `ETag` (a resource-version identifier), then send it on the next request as `If-None-Match: "<etag>"`. If nothing changed, the server returns 304 with no body and you reuse your cached copy; if it changed, you get 200 with a fresh body and a new ETag.

ETags come in strong form (`"..."`) and weak form (`W/"..."`). A weak ETag treats semantically-equivalent representations as equal, which makes it unsuitable for byte-exact operations like Range requests.

The time-based alternative is `Last-Modified` + `If-Modified-Since`, but it only has one-second resolution. On writes (PUT/DELETE), conditional requests use `If-Match` for optimistic locking, and a mismatch yields 412 Precondition Failed.

Related headers

Related status codes