Skip to content
Lesson 13 of 16

Step 1 of 5 · Reading · ~3 min

Read

Caching, Compression & Beyond

Conditional Requests & ETags

Browsers and CDNs revalidate cached responses before refetching them. A conditional request lets the client say "give me this only if it changed", and the server answers with a tiny 304 Not Modified instead of the whole body. Two mechanisms do it: hash-based (ETag with If-None-Match) and time-based (Last-Modified with If-Modified-Since). Your program will evaluate the preconditions on a request against the current version of a resource and answer with the right status.

The validators

HTTP/1.1 200 OK
ETag: "a1b2c3"                              <- opaque identifier of this exact version
Last-Modified: Tue, 15 Nov 2022 12:00:00 GMT

An entity-tag is a quoted opaque string, optionally prefixed W/. A strong tag ("a1b2c3") promises byte-for-byte equality. A weak tag (W/"a1b2c3") promises only that the two versions are semantically interchangeable (the same page gzip-compressed or not). Servers usually derive strong tags from a hash of the content.

The tag is opaque and may contain commas, so a header such as If-None-Match: "x,y", "z" holds two tags, not three. Split by walking the quotes, never by splitting on commas.

Two comparison functions

  • Strong comparison (used by If-Match): both tags must be strong and their opaque strings equal.
  • Weak comparison (used by If-None-Match): ignore the W/ prefixes and compare the opaque strings.

W/"v1" and "v1" therefore match weakly and never match strongly.

What each header means

HeaderPasses whenWhen it fails
If-Match: "a", "b" or *some listed tag strong-matches the current tag, or *412 Precondition Failed
If-None-Match: "a", W/"b" or *no listed tag weak-matches, and not *GET/HEAD: 304 Not Modified; other methods: 412
If-Modified-Since: <date>the resource changed after the date304 (GET/HEAD only)

If-Match protects writes from lost updates ("change this only if it is still version X"). If-None-Match: * on a PUT means "create only, never overwrite".

Order of evaluation (RFC 9110 section 13.2.2)

  1. If-Match, if present. Failure: 412.
  2. If-None-Match, if present. Failure: 304 for GET and HEAD, 412 otherwise.
  3. Only when If-None-Match is absent: If-Modified-Since, for GET and HEAD, with a date that parses. Resource not modified since: 304.
  4. Otherwise the request proceeds: 200 OK.

The ETag wins over the date because the date has one-second resolution and trusts two clocks to agree: a file changed twice within a second looks unchanged. A date that does not parse as an HTTP date is ignored, never an error.

An HTTP-date in the preferred format is exactly 29 characters: Tue, 15 Nov 2022 12:00:00 GMT. Compare two of them by year, month, day, hour, minute, second.

What the response carries

  • 200 OK: ETag and Last-Modified, then the body.
  • 304 Not Modified: no body, and it repeats the ETag so the cache can refresh its entry.
  • 412 Precondition Failed: no validators needed.

The contract

The first two input lines (LF terminated) describe the current resource: ETAG <tag> and LASTMOD <http-date>. The rest is the raw request (CRLF). Print the status line and then, for 200, ETag: ... and Last-Modified: ...; for 304, ETag: ...; for 412 nothing more.

Common mistakes

  • Splitting If-None-Match on every comma, which breaks tags that contain one.
  • Using strong comparison for If-None-Match, so weak clients never get a 304.
  • Letting a matching If-Modified-Since override a non-matching If-None-Match.
  • Sending a body, or omitting the ETag, on a 304.

Your exercise

The starter reads the resource and the request line and headers for you. You implement the two comparisons, the tag list walk, the date comparison and the order of evaluation.

Up nextRange Requests & Partial ContentCaching, Compression & Beyond

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…