Validator Merging

When Relaycache has a cached entry and the client sends its own conditional headers, both sets of validators must be forwarded to the origin so that either can trigger a 304. The origin’s 304 response then tells us whose cache was current, allowing Relaycache to return the right status to the client.

ETag (If-None-Match)

If-None-Match accepts a comma-separated list of ETags (RFC 7232 §3.2):

If-None-Match: "client-etag", "proxy-etag"

The origin responds 304 if either matches, and echoes the winning ETag in its 304 response headers.

Merge rules

Client has If-None-Match

Proxy has cached ETag

Action

No

No

nothing

Yes

No

forward client’s header unchanged

No

Yes

inject proxy’s ETag

Yes, same value

Yes, same

forward unchanged (already identical)

Yes, "A"

Yes, "B"

merge: "A", "B"

Determining who matched

After a 304, Relaycache checks the ETag returned by the origin:

Returned ETag in client's If-None-Match list?
  Yes → client's cache is current → return 304 to client
  No  → proxy's ETag matched     → return 200 with cached body

If the origin returns no ETag in the 304, fall through to the date check.

Last-Modified (If-Modified-Since)

If-Modified-Since can only carry one date. Relaycache sends max(client_date, proxy_date) — the newer of the two.

Why max?

Let:

  • C = client’s If-Modified-Since date

  • P = proxy’s cached Last-Modified date

  • U = actual last-modified date on the origin (unknown to us)

The origin returns 304 when U forwarded_date.

Case C > P (client is newer than proxy cache)

The client has a version we don’t have cached.

Send

Origin says

Meaning

Correct?

C (max)

304 if U ≤ C

Client is current

C (max)

200 if U > C

Both stale; update cache

P (min)

304 if U ≤ P

Both current (P < C so U ≤ P ≤ C)

✅ but rare

P (min)

200 if P < U ≤ C

Wasteful: we’d download a body the client already has

❌ wasted bandwidth

Verdict: send C.

Case C < P (proxy is newer — the common case)

The proxy has a version the client doesn’t have yet.

Send

Origin says

Meaning

Correct?

P (max)

304 if U ≤ P

Proxy is current; serve cached body as 200 to client

P (max)

200 if U > P

Both stale; update cache

C (min)

304 if U ≤ C

Client is current — but C < P so this is a contradiction (the proxy can’t have cached a body newer than the origin’s current version)

impossible

C (min)

200 if C < U ≤ P

Wasteful: we’d download a body we already have in cache

❌ wasted bandwidth

Verdict: send P.

Summary: always send max(C, P)

Sending the newer date is strictly better in the asymmetric cases and neutral in the symmetric case (C == P). max(C, P) minimises unnecessary 200 responses.

Determining who matched (date fallback)

When no ETag is present in the 304, compare the origin’s Last-Modified (from the 304 headers) against the client’s If-Modified-Since:

origin Last-Modified ≤ client If-Modified-Since?
  Yes → client's cache is current → return 304
  No  → proxy's date matched      → return 200 with cached body

When no usable validators exist at all, Relaycache conservatively assumes the proxy matched and returns 200 with the cached body. This is always correct (the client gets valid content) at the cost of one unnecessary body transfer.