How-To

ETag and If-None-Match: Handling HTTP 304

An authorized polling job can send a previous ETag in If-None-Match to ask whether it needs another response body. HTTP 304 means reuse the correct stored representation; it doesn't supply a replacement body. The request still happened, so keep it in your request budget.

Hire a Clawd is our own service. This guide separates documented HTTP behavior from practical workflow recommendations. It makes no claim about measured savings on a target service.

An ETag belongs to a representation

Documented behavior: an ETag is an opaque validator. Copy its value exactly, including quotes and any W/ weak prefix. For GET, If-None-Match uses weak comparison: a matching validator can produce 304 Not Modified, which has no response body. Weak equivalence doesn't promise identical bytes. See RFC 9110's ETag definition, If-None-Match rules and 304 semantics.

When the representation changes and the validator no longer matches, a successful GET normally returns 200 with the current body. A 200 alone doesn't prove a change: the server might ignore the condition. Authorization failures and other errors remain possible.

Inspect the first response, then send its tag

Replace the example URL with an endpoint you're authorized to read. Choose a permitted test resource that doesn't need credentials for this demonstration, or use your service's approved authentication procedure separately. These examples don't set up authentication.

curl --disable --silent --show-error --include --max-time 15 'https://your-authorized-host.example/catalog.json'

Inspect the status, headers and body. If the successful response includes an ETag, replace the second command's entire example value W/"catalog-v1" with that actual value. Don't invent a tag when the header is absent. Keep the URL and relevant request context the same.

curl --disable --silent --show-error --include --max-time 15 --header 'If-None-Match: W/"catalog-v1"' 'https://your-authorized-host.example/catalog.json'

These commands print responses, including headers. They don't maintain a cache, overwrite files, retry or follow redirects. --disable comes first to ignore local curlrc settings; each transfer has a 15-second limit. Proxy environment settings can still apply. Errors can appear on standard error. See the official curl option reference.

They don't use --fail, so inspect the HTTP status as well as curl's exit code. A completed transfer carrying 403 or 429 isn't a successful refresh. A redirect also needs inspection before you choose a new destination.

Tested against an owned loopback fixture on 2026-10-20. Six checks ran the commands extracted from this article, replacing only the URL and isolating proxy configuration. They verified an initial 200, matching 304 with no new body, changed 200, missing ETag, and visible 403/429 responses without retries. This isn't a test of your service, a complete cache or a performance benchmark.

Storage permission comes before reuse

Documented cache rules: Vary identifies request fields that must match when selecting a stored response; Vary: * never matches. A response's Cache-Control: no-store forbids cache storage. Unqualified no-cache allows storage but requires successful validation before reuse. See RFC 9111, section 4.1, no-store and no-cache. These aren't the only cache requirements; use an established HTTP cache for production behavior.

Practical recommendation: review one real request pair before adding conditional polling to a job. A catalog requested in French shouldn't silently feed an English-language report. Treat content negotiation settings as part of the job's inputs, and check which request fields the service actually varies on.

Keep account and tenant data isolated even when two jobs use the same URL. For example, a buyer's private catalog shouldn't become another buyer's fallback after a login change. Review account boundaries explicitly rather than assuming Vary describes every privacy boundary. Avoid putting credentials or private response bodies in diagnostic logs.

Decide what the job does with each outcome

Plan separate handling for a new body, a validated stored body and a failed refresh. A job that saved only the tag has nothing useful to read after a 304. Don't let an empty response replace a previously populated report. If the stored body is missing, stop that refresh and arrange an authorized unconditional fetch within the normal request budget.

For a changed 200 response, validate the expected content before handing it to downstream work. Keep the body and its validator associated so an interrupted update can't pair yesterday's data with today's tag. Those are application design recommendations; the two commands above don't implement them.

Count conditional requests alongside other traffic unless the provider explicitly documents an exemption. There's no guaranteed server CPU saving: the service may still perform expensive work before deciding its response. Measure your own workflow before promising lower costs or raising polling frequency.

For refusals or throttling, follow the 403, 429 and Retry-After guide. Don't bypass access controls or disable security checks to obtain a validator. If the permitted polling rate can't meet the job's needs, ask the operator about an export or supported change feed.

If you need help deciding whether a permitted source fits your workflow, ask for a scoped assessment with its documented limits and a redacted request/response example. Don't include credentials or private customer data.