WEB / WEB-09.03
ETags and conditional requests
A validator turns an earlier representation observation into a request condition. Distinguish 304 body reuse from an If-Match write guard.
1. Core · Mechanism
Read every scenario without motion
304, body retained
- Select stored revision
Duru requests validation using cached Çay and atlas-v1; current origin v1 is supplied.
- Browser / Duru
- Conditional GET ready
- Origin / Atlas
- Request absent
- Local HTTP cache
- Stored Çay; v1; max-age0
- Origin representation
- Current Çay; v1
- Conditional GET arrived
The origin receives If-None-Match W/atlas-v1; normal checks and supplied context are appropriate.
- Browser / Duru
- Waiting validation
- Origin / Atlas
- GET received; no decision yet
- Local HTTP cache
- Stored Çay; v1; max-age0
- Origin representation
- Current Çay; v1
- Weak comparison
Current strong v1 matches supplied weak v1; the none-match condition is false and 304 is prepared.
- Browser / Duru
- Waiting validation
- Origin / Atlas
- 304 ready; content0; metadata updated
- Local HTTP cache
- Stored Çay; v1; max-age0
- Origin representation
- Current Çay; v1
- 304 arrived
The browser receives Date, ETag, Vary, and max-age60 metadata with zero origin content transfer.
- Browser / Duru
- 304 received; content0
- Origin / Atlas
- 304 ready; content0
- Local HTTP cache
- Stored Çay; v1; max-age0
- Origin representation
- Current Çay; v1
- Update matching entry
The local cache selects the supplied matching strong entry, updates header values, and retains Çay.
- Browser / Duru
- 304 processed locally
- Origin / Atlas
- Exchange complete
- Local HTTP cache
- Stored Çay; v1; new Date; max-age60
- Origin representation
- Current Çay; v1
- Use retained body
Duru uses the retained four-byte body locally; this is not a new body arrival from the origin.
- Browser / Duru
- Effective content: Çay; retained4 bytes
- Origin / Atlas
- Exchange complete
- Local HTTP cache
- Stored Çay; v1; new Date; max-age60
- Origin representation
- Current Çay; v1
Changed, full 200
- Old v1 observation
Duru begins validation using old Çay/v1 while the origin currently has v2 and Atlas note.
- Browser / Duru
- Conditional GET ready
- Origin / Atlas
- Request absent
- Local HTTP cache
- Stored Çay; v1
- Origin representation
- Current Atlas note; v2
- GET arrived
The origin receives the old v1 validator; request arrival does not change the cached body.
- Browser / Duru
- Waiting response
- Origin / Atlas
- GET received; If-None-Match v1
- Local HTTP cache
- Stored Çay; v1
- Origin representation
- Current Atlas note; v2
- Different representation
Opaque values differ, making none-match true; a full v2 representation is prepared with 200.
- Browser / Duru
- Waiting response
- Origin / Atlas
- 200 ready; Atlas note; v2
- Local HTTP cache
- Stored Çay; v1
- Origin representation
- Current Atlas note; v2
- 200 content arrived
Ten UTF-8 bytes of new content and the v2 tag reach the browser; cached entry awaits local replacement.
- Browser / Duru
- 200 received; Atlas note10; v2
- Origin / Atlas
- 200 ready; Atlas note; v2
- Local HTTP cache
- Stored Çay; v1
- Origin representation
- Current Atlas note; v2
- Replace copy
Supplied storage eligibility is satisfied, so the cache writes the new body and tag together.
- Browser / Duru
- Use new content
- Origin / Atlas
- Exchange complete
- Local HTTP cache
- Stored Atlas note; v2
- Origin representation
- Current Atlas note; v2
- New observation
Duru now has a v2 validator observation; the v1 body is not kept under the new tag.
- Browser / Duru
- Effective content Atlas note; v2
- Origin / Atlas
- Exchange complete
- Local HTTP cache
- Stored Atlas note; v2
- Origin representation
- Current Atlas note; v2
Stale write, 412
- Old revision edit
Duru prepares Updated note against v1; permission is supplied true and the origin currently has v2.
- Browser / Duru
- PUT ready; If-Match v1
- Origin / Atlas
- Request absent
- Local HTTP cache
- Observed v1; no cache mutation
- Origin representation
- Current Atlas note; v2
- PUT arrived
The origin receives inert replacement content and the old strong validator without applying the method.
- Browser / Duru
- Waiting outcome
- Origin / Atlas
- PUT received; not applied
- Local HTTP cache
- Observed v1; no cache mutation
- Origin representation
- Current Atlas note; v2
- Normal checks passed
The fixture supplies passed normal/permission checks; validator comparison does not replace that decision.
- Browser / Duru
- Waiting outcome
- Origin / Atlas
- Checks passed; compare pending
- Local HTTP cache
- Observed v1; no cache mutation
- Origin representation
- Current Atlas note; v2
- Strong condition false
v1 and v2 do not strongly match; with supplied already-applied false, 412 is selected without a write.
- Browser / Duru
- Waiting outcome
- Origin / Atlas
- 412 ready; methodApplied=false
- Local HTTP cache
- Observed v1; no cache mutation
- Origin representation
- Current Atlas note; v2
- 412 arrived
Duru receives precondition failure while the origin v2 representation remains unchanged.
- Browser / Duru
- 412 received; edit not applied
- Origin / Atlas
- 412 ready; methodApplied=false
- Local HTTP cache
- Observed v1; no cache mutation
- Origin representation
- Current Atlas note; v2
- Assess conflict
A new read and business reconciliation are separate actions; the scene does not automatically reapply the stale edit.
- Browser / Duru
- Needs current-state read/reconciliation
- Origin / Atlas
- Exchange complete
- Local HTTP cache
- Observed v1 remains old
- Origin representation
- Current Atlas note; v2
ACTORS · STATE · DATA FLOW
An ETag describes a representation observation
Duru has stored the Çay body with ETag: "atlas-v1" metadata. This opaque value validates the current selected representation; it is not a password, object-owner ID, or permission. Clients need not know its generation method and should not infer timestamps or identity from v1. The instructional names merely make revisions easy to read.
A strong tag must change when representation data changes; W/ marks a weak validator. Weak comparison compares opaque values while ignoring whether either tag is weak. Strong comparison requires both tags to be non-weak and their opaque values to match. "atlas-v1" and W/"atlas-v1" therefore match weakly but not strongly.
Validators are scoped to selected representations. Gzip and uncoded representations of one resource with different bytes cannot share a strong tag; transfer coding is different from representation coding. Different resources can share a tag without proving equal content or permissions. The artifact fixes one target, text/plain, and identity coding.
Conditional GET: can the earlier body still be used?
Duru sends GET with If-None-Match: W/"atlas-v1". With current tag "atlas-v1", weak comparison matches: the none-match condition is false and 304 replaces normal GET content. If-None-Match uses weak comparison for GET/HEAD; listing a strong tag does not select strong comparison. A false matching condition on other methods produces 412.
A 304 carries no content or trailers. It includes Date, ETag, Vary, Cache-Control, and other applicable required fields that a 200 to the same request would send. The artifact updates Date and max-age. Duru’s cache first identifies the stored representation eligible for this update; a matching strong cached entry is supplied. Header values change while the Çay body remains.
If the origin instead selects "atlas-v2" with Atlas note, the old v1 condition is true and a full 200 arrives. The cache uses the new response and may replace its entry when storage is eligible. It must not store a 304 as an empty representation or leave new 200 content under the old tag. WEB-09.02 age arithmetic applies again to updated timing metadata; a validator alone is not a freshness lifetime.
If-Match: write against an earlier state
Duru prepared a benign note edit against v1 while the origin now has v2. If-Match: "atlas-v1" makes PUT conditional on a strong match with the current tag. After the supplied normal request/permission checks pass, the precondition is evaluated before applying the method. This fixture chooses 412, applies no update, and retains v2.
W/"atlas-v2" has the same opaque value but cannot pass a strong If-Match guard. Do not use a weak validator as an exact write revision. The RFC also describes a success-response possibility for a false If-Match when a change is already applied; duplicate/already-applied is supplied false here. Follow this branch’s selected rejection contract rather than claiming every false condition universally requires 412.
Lost-update protection requires more than comparison code. A production store must connect the current-version check and write in one atomic boundary; an intervening writer would invalidate the intended condition. This lesson uses a sequential in-process representation fixture rather than measuring database isolation or lock performance. Reading current state and resolving a business conflict differs from blindly accepting a stale edit.
2. Red · Trust & Impact
Read every scenario without motion
Tag scope
- Opaque-tag knowledge was separated from identity
atlas-v1 is a public representation validator. Knowing it supplies neither an authenticated user nor a resource grant. If-Match and If-None-Match compare selected representation state, not user permission.
- Validator record
- Tag known / identity-permission not inferred
- Precondition gate
- Comparison pending
- Method/body boundary
- No verdict
- Strong and weak matches gave different guarantees
W/"atlas-v1" and "atlas-v1" match under weak comparison but not strong comparison. The header’s required comparison matters. Interpreting a weak match as exact byte equality creates the wrong control.
- Validator record
- Tag known / identity-permission not inferred
- Precondition gate
- Weak pair: weak true / strong false
- Method/body boundary
- No verdict
- Matching and method execution were different results
An If-None-Match match makes the condition false, yielding 304 for GET here. An If-Match match makes its condition true. Even with the same tag, these headers answer opposite precondition questions.
- Validator record
- Tag known / identity-permission not inferred
- Precondition gate
- Weak pair: weak true / strong false
- Method/body boundary
- If-None-Match match→false / If-Match match→true
- Encoded-variant tags were tied to separate representations
The identity variant has strong tag "tea-plain-a" and gzip variant "tea-gzip-a", with different encoded bytes supplied. One resource URL does not imply byte-for-byte equality of all representations.
- Validator record
- Identity tag tea-plain-a / gzip tag tea-gzip-a
- Precondition gate
- Weak pair: weak true / strong false
- Method/body boundary
- If-None-Match match→false / If-Match match→true
- Normal request gates retained their role before preconditions
Normal authentication and resource/action authorization require separate evaluation. put412 supplies normalChecksPassedSupplied true, not a real permission engine. A correct validator cannot repair wrong user policy.
- Validator record
- Identity tag tea-plain-a / gzip tag tea-gzip-a
- Precondition gate
- Normal checks supplied / validator is not permission
- Method/body boundary
- If-None-Match match→false / If-Match match→true
- Comparison results were not atomic-persistence claims
The ledger shows sequential in-process state. Actual concurrent compare/write atomicity needs separate verification. Consistent validator semantics do not establish database isolation, protected writes or production permission.
- Validator record
- Identity tag tea-plain-a / gzip tag tea-gzip-a
- Precondition gate
- Normal checks supplied / validator is not permission
- Method/body boundary
- Validator scope known / atomic write unmeasured
Body association
- The stored candidate retained its body and tag
storedBefore has tag "atlas-v1", body Çay and contentOctets 4. Matching Vary and one complete stored candidate are supplied. URL text alone is insufficient to select which representation to update.
- Validator record
- Selected stored v1 / Çay / 4 bytes
- Precondition gate
- Comparison pending
- Method/body boundary
- No verdict
- A 304 metadata update was not body replacement
304 has actualContentOctets 0. Date and Cache-Control metadata of the matching stored candidate are updated while its body stays Çay. Empty origin content does not require deleting the stored body.
- Validator record
- Selected stored v1 / Çay / 4 bytes
- Precondition gate
- 304: metadata updated / existing body retained
- Method/body boundary
- No verdict
- Cached effective content was separated from origin bytes
The origin sends no body with 304, but the selected stored response can provide four bytes of Çay to the reader. Merging these counts creates the wrong empty-content verdict. Actual browser-cache behavior is not measured here.
- Validator record
- Selected stored v1 / Çay / 4 bytes
- Precondition gate
- 304: metadata updated / existing body retained
- Method/body boundary
- Origin body 0 / retained content 4
- A changed 200 brought its new body and tag together
changed200 supplies currentTag "atlas-v2", body Atlas note and contentOctets 10. Old v1 no longer matches. Leaving old v1 metadata with the new body would misassociate later validation.
- Validator record
- Changed 200 / v2 / Atlas note / 10 bytes
- Precondition gate
- 304: metadata updated / existing body retained
- Method/body boundary
- Origin body 0 / retained content 4
- Replacement stayed scoped to the selected candidate
Tag and body are replaced together without spreading metadata to every URL copy before matching context is checked. 304 retention and 200 replacement are different local-store decisions. Receipt is also separate from store update.
- Validator record
- Changed 200 / v2 / Atlas note / 10 bytes
- Precondition gate
- Replace tag and body together / candidate scoped
- Method/body boundary
- Origin body 0 / retained content 4
- Body association did not guarantee confidentiality
Retention and replacement are explicit in this public representation ledger. Private-cache recipients and protected-object policy require separate evidence. Correct metadata/body association does not guarantee cache correctness for every user context.
- Validator record
- Changed 200 / v2 / Atlas note / 10 bytes
- Precondition gate
- Replace tag and body together / candidate scoped
- Method/body boundary
- Association consistent / recipient policy separate
ASSUMPTION · BOUNDARY · CONTROL
Do not substitute a validator for permission
An ETag can appear in a public response. Knowing it grants no right to change a resource; If-Match compares an observed representation state with current state. The failure condition is treating a matching tag as authentication or object permission. A correct precondition cannot repair an incorrect user policy.
Consequences include applying an update to the correct revision on behalf of the wrong actor, or selecting the wrong cached representation. Resolve trusted identity and resource/action authorization during normal request handling, then evaluate applicable preconditions before applying the method. Public tea and supplied permissionChecksPassed isolate normal semantics here; they are not a real permission engine.
Treating weak match as byte equality is a separate error. Weak semantics can support cache-validation substitution without providing the guarantee required for an exact write or partial range. Record which precondition uses which comparison.
Keep metadata and body associated
A bodyless 304 does not ask the cache to delete Çay. Failure conditions include updating all URL copies without matching context, clearing retained content, or associating new200 content with old v1 metadata. Effects include wrong displays and incorrect future validation; privacy impact depends on the selected representation.
Track candidate selection, validator matching, required header updates, and body retention/replacement together. With stale If-Match, 412 leaves mutation false. Verify compare/write atomicity separately for real concurrency. These scenes do not provide exploitation, replay, or unauthorized editing exercises.
3. Blue · Secure development & defense
Read every scenario without motion
304 retain
- Conditional GET retained its comparison rule
The origin fixture produces response304 and changed200; the local-cache ledger produces storedBefore and storedAfter304. PUT separates requestedBody from methodApplied/afterBody. Zero origin-content bytes do not erase four retained cached bytes, and a request body does not prove a write. Correlate byte counts and state results across their distinct producers. GET If-None-Match W/"atlas-v1" weakly matches current strong "atlas-v1". matches is true but conditionTrue false. Review reads the comparison together with the header’s logic.
- Conditional ledger
- Weak match true / If-None-Match condition false
- Comparison/state review
- Comparison pending
- Evidence verdict
- No verdict
- Origin response content was counted separately
response304 has status 304 and actualContentOctets 0, with new Date 00:01:00 and Cache-Control max-age=60. No body is invented for this valid response without Content-Length; headers are not content.
- Conditional ledger
- Weak match true / If-None-Match condition false
- Comparison/state review
- 304 origin content 0 / metadata refreshed
- Evidence verdict
- No verdict
- The stored body remained an independent observation
storedAfter304 retains status 200, body Çay and contentOctets 4. Date and max-age update while ETag v1 and media association remain. Stored status 200 and origin status 304 belong to different records.
- Conditional ledger
- Stored 200 / Çay 4 / metadata updated
- Comparison/state review
- 304 origin content 0 / metadata refreshed
- Evidence verdict
- No verdict
- The body oracle used normal and positive controls
Normal XML gives actualContentOctets 0 for 304 and count zero. Positive XML reports one content byte and count one. The oracle tests the body rule without claiming a real captured response.
- Conditional ledger
- Stored 200 / Çay 4 / metadata updated
- Comparison/state review
- Normal body-rule count 0 / positive 1
- Evidence verdict
- No verdict
- A missing response event made the verdict unknown
If a trace lacks response observations, zero matches are not complete 304 evidence. The normal fixture’s body-0 record cannot fill a missing capture. Origin-content and cached-effective-content scopes need separate verification.
- Conditional ledger
- Stored 200 / Çay 4 / metadata updated
- Comparison/state review
- Normal body-rule count 0 / positive 1
- Evidence verdict
- UNKNOWN without response-content coverage
- Regression retained representation context
Review rechecks selected candidate, Vary context, validator comparison and header update together. Ranges, a general tag parser and browser caches are not measured. Sequential-ledger results are not transferred to all deployments without new runtime evidence.
- Conditional ledger
- Stored 200 / Çay 4 / metadata updated
- Comparison/state review
- Normal body-rule count 0 / positive 1
- Evidence verdict
- Recheck candidate/context/validator/metadata
412 no mutation
- The requested write was separated from current state
The origin fixture produces response304 and changed200; the local-cache ledger produces storedBefore and storedAfter304. PUT separates requestedBody from methodApplied/afterBody. Zero origin-content bytes do not erase four retained cached bytes, and a request body does not prove a write. Correlate byte counts and state results across their distinct producers. PUT carries requestedBody Updated note while currentBody is Atlas note with currentTag "atlas-v2". If-Match asks for "atlas-v1". Having a request body does not prove that the method was applied.
- Conditional ledger
- Requested Updated note / current v2 Atlas note
- Comparison/state review
- Comparison pending
- Evidence verdict
- No verdict
- The strong precondition was checked before method application
Normal checks passed and alreadyApplied false are supplied. Strong v1-v2 matching is false, making conditionTrue false. This case returns 412; a failed precondition and application denial are different reasons.
- Conditional ledger
- Requested Updated note / current v2 Atlas note
- Comparison/state review
- Strong mismatch / condition false / 412
- Evidence verdict
- No verdict
- The method’s state effect was recorded separately
methodApplied is false; afterTag remains "atlas-v2" and afterBody Atlas note. Error status alone does not measure preservation; the ledger provides separate before/after fields. The requested edit remains inert data.
- Conditional ledger
- After v2 / Atlas note / methodApplied=false
- Comparison/state review
- Strong mismatch / condition false / 412
- Evidence verdict
- No verdict
- The comparison matrix provided negative controls
Strong v1-v1 true, weak-v1/strong-v1 strong false and v1-v2 false are separate controls. The matrix shows why a write’s strong precondition must not be replaced by weak cache comparison.
- Conditional ledger
- After v2 / Atlas note / methodApplied=false
- Comparison/state review
- Strong-write comparison retained
- Evidence verdict
- No verdict
- No mutation was not a concurrency claim
The failed guard preserves state for this one sequential operation. Concurrent compare/write isolation, retries and a real permission engine are unmeasured. Actual update correctness additionally needs review of the guard/write atomic boundary.
- Conditional ledger
- After v2 / Atlas note / methodApplied=false
- Comparison/state review
- Strong-write comparison retained
- Evidence verdict
- Sequential state retained / concurrency unmeasured
- Evidence collection did not duplicate bodies and secrets
Status, comparison class, methodApplied and scoped revision association may suffice for review without full private notes or credentials. A new runtime requires separate observation of the response and actual state effect.
- Conditional ledger
- After v2 / Atlas note / methodApplied=false
- Comparison/state review
- Strong-write comparison retained
- Evidence verdict
- Needed: status/comparison/state-effect scope
IMPLEMENTATION · SECURITY CONTROL · VERIFICATION
Keep two byte counts for 304
The origin fixture produces response304 and changed200; the local-cache ledger produces storedBefore and storedAfter304. PUT separates requestedBody from methodApplied/afterBody. Zero origin-content bytes do not erase four retained cached bytes, and a request body does not prove a write. Correlate byte counts and state results across their distinct producers. storedBefore contentOctets=4 and response304 actualContentOctets=0 are different observations. Find the new Date and Cache-Control, then confirm storedAfter still contains Çay. Empty response content was not written as a replacement body. Header updates occur inside the browser cache rather than transferring new origin content.
The changed200 branch has Atlas note and tag atlas-v2; its ten-byte representation replaces the cached copy only after response arrival and a local storage update. In PUT412 the requested edit is inert and current v2 remains. Separating the server’s precondition decision from client status arrival makes causality explicit.
The selector finds status304 with actualContentOctets>0. Normal0/positive1/missing0 are synthetic observations testing the body rule. Missing events cannot establish a clean result; real evidence requires capture completeness and method correlation. The XML fields are not presented as product log fields.
Apply the precondition table manually
For the same strong pair, record strong/weak true; with a weak side, strong false/weak true; with different opaque values, both false. Then apply the header logic: a match makes If-None-Match false but If-Match true. Finding a match is not the same statement as applying the requested method.
Separate normal checks, selected representation, validator comparison, method application, and cached result in a review ledger. A 304 means the origin transferred no body; do not count the browser’s effective displayed content as zero. Even a native normal exchange alone cannot verify all browser caching, concurrency, or authorization behavior.
A write handler must compare If-Match with the current representation’s strong validator before applying the change. Here client atlas-v1 differs from current atlas-v2: producing 412 must not persist Updated note or replace the current body. Header presence alone does not prove enforcement, and a matching validator grants no user permission; ordinary permission checks remain separate. Verify retained body during 304 metadata updates and unchanged state when a write precondition is rejected.
NOW APPLY IT TO THE CASE
How do weak GET validation and strong write preconditions yield different results?
A stored v1 copy of Çay receives 304 after a matching conditional GET: the origin sends no body and the stored four-byte body is retained. A PUT with stale v1 If-Match against current v2 receives 412 and leaves Atlas note unchanged.
Which cache owner supplies the case representation? Evaluate key scope, freshness and viewer context together; a hit does not establish the correct viewer.
Open the related case file ↗READ THE EXAMPLE TOGETHER
Validator, metadata, and body-association ledger
Select a field and examine what it supports and where the evidence ends.
Read the raw artifact and field explanations
{
"target": "/tea?view=plain",
"selectedContext": {
"accept": "text/plain",
"contentCoding": "identity",
"varyMatchesSupplied": true,
"oneCompleteStoredCandidate": true
},
"storedBefore": {
"status": 200,
"body": "Çay",
"contentOctets": 4,
"headers": {
"date": "Thu, 08 Oct 2026 00:00:00 GMT",
"etag": "\"atlas-v1\"",
"cache-control": "max-age=0",
"vary": "Accept",
"content-type": "text/plain; charset=utf-8",
"content-length": "4"
}
},
"response304": {
"requestMethod": "GET",
"ifNoneMatch": "W/\"atlas-v1\"",
"currentTag": "\"atlas-v1\"",
"comparison": "weak",
"matches": true,
"conditionTrue": false,
"status": 304,
"actualContentOctets": 0,
"headers": {
"date": "Thu, 08 Oct 2026 00:01:00 GMT",
"etag": "\"atlas-v1\"",
"cache-control": "max-age=60",
"vary": "Accept"
},
"http11": "HTTP/1.1 304 Not Modified\r\nDate: Thu, 08 Oct 2026 00:01:00 GMT\r\nEtag: \"atlas-v1\"\r\nCache-Control: max-age=60\r\nVary: Accept\r\n\r\n"
},
"storedAfter304": {
"status": 200,
"body": "Çay",
"contentOctets": 4,
"headers": {
"date": "Thu, 08 Oct 2026 00:01:00 GMT",
"etag": "\"atlas-v1\"",
"cache-control": "max-age=60",
"vary": "Accept",
"content-type": "text/plain; charset=utf-8",
"content-length": "4"
}
},
"changed200": {
"ifNoneMatch": "\"atlas-v1\"",
"currentTag": "\"atlas-v2\"",
"matches": false,
"conditionTrue": true,
"status": 200,
"body": "Atlas note",
"contentOctets": 10,
"cachedAfterTag": "\"atlas-v2\"",
"cachedAfterBody": "Atlas note"
},
"put412": {
"method": "PUT",
"ifMatch": "\"atlas-v1\"",
"currentTag": "\"atlas-v2\"",
"currentBody": "Atlas note",
"requestedBody": "Updated note",
"comparison": "strong",
"matches": false,
"conditionTrue": false,
"normalChecksPassedSupplied": true,
"alreadyAppliedSupplied": false,
"status": 412,
"methodApplied": false,
"afterTag": "\"atlas-v2\"",
"afterBody": "Atlas note"
},
"comparisonRows": [
{
"left": "\"atlas-v1\"",
"right": "\"atlas-v1\"",
"strong": true,
"weak": true
},
{
"left": "W/\"atlas-v1\"",
"right": "\"atlas-v1\"",
"strong": false,
"weak": true
},
{
"left": "\"atlas-v1\"",
"right": "\"atlas-v2\"",
"strong": false,
"weak": false
}
],
"representationVariantExample": {
"resource": "/tea?view=plain",
"identityStrongTag": "\"tea-plain-a\"",
"gzipStrongTag": "\"tea-gzip-a\"",
"differentEncodedBytesSupplied": true
},
"selector": {
"language": "XPath",
"xml": "<observations><response status=\"304\" actualContentOctets=\"0\"/></observations>",
"xpath": "count(/observations/response[@status=\"304\" and number(@actualContentOctets)>0])",
"expected": 0,
"positiveFixtureXml": "<observations><response status=\"304\" actualContentOctets=\"1\"/></observations>",
"positiveExpected": 1,
"scope": "Only authored protocol-decision observations, not product log field names or a live HTTP capture.",
"missingFieldBehavior": "Missing observation is insufficient evidence; zero matches is not proof of correct runtime behavior."
}
}- comparisonRows
- Opaque comparison results; precondition truth is determined separately.
- response304 / storedAfter304
- Origin content0; matching stored body4 is retained while Date/policy update.
- changed200
- New tag/body replace together; the old copy does not match.
- put412
- Normal checks passed and duplicate false are supplied; the failed strong guard applies no method.
Representation context and trusted decisions are supplied. One sequential in-process state; no actual browser-cache, production permission, database isolation, range, or general tag parser is measured.
Two reality checks
Knowing an ETag grants write permission; W/ is sufficient for strong If-Match.
A validator grants no permission. If-Match requires strong comparison and a weak side cannot strongly match even the same opaque value.
304 is an empty representation and the cached body must be deleted.
304 carries no content; eligible matching stored-response metadata updates and its existing body can be reused.
YOUR TURN
Work through this story.
Predict the outcome, find the evidence boundary, and explain it in your own words.
If-Match is atlas-v1 but currentTag is atlas-v2. Is the selected PUT write applied?
- Yes; the precondition is only advisory because normal checks passed.
- No; 412 and methodApplied=false.
Read the explained answer
No; 412 and methodApplied=false. — Even though normal checks passed, strong-tag matching fails; the current body/tag are preserved.
How is Çay supplied when 304 has no content?
Selected lesson artifact fields; teaching example
- response304.actualContentOctets
- 0
- storedAfter304.body
- Çay
- storedAfter304.headers.cache-control
- max-age=60
- The stored body is retained; 304 updates metadata on the matching entry.
- The 304 response body implicitly contains Çay.
Read the explained answer
The stored body is retained; 304 updates metadata on the matching entry. — The selected complete candidate already has the Çay body; max-age 60 metadata is updated, but the body is not carried in 304.
Why can W/atlas-v1 and atlas-v1 match for If-None-Match GET but not under strong If-Match comparison?
Read the explained answer
Weak comparison can match the opaque tag value despite differing weak markers. Strong comparison requires both tags to be strong and their values to match. Representation variants may have distinct strong tags for different encoded bytes; a shared resource URL does not make every variant byte-equal.
□ I linked weak/strong mode to the relevant header.
□ I separated the resource from the encoded representation.
Open the application console · request / response / browser
Also open request, URL, cookie and DOM examples
Sources and scope
- RFC 9110 — HTTP Semantics1.3 Core Semantics · 7.1 Determining the Target Resource · 8.6 Content-Length · 8.8.1 Weak versus Strong · 8.8.3 ETag · 8.8.3.2 Comparison · 8.8.3.3 Example: Entity Tags Varying on Content-Negotiated Resources · 13.1.1 If-Match · 13.1.2 If-None-Match · 13.2.1 When to Evaluate · 15.4.5 304 Not Modified · 15.5.13 412 Precondition Failed
- RFC 9111 — HTTP Caching1 Introduction · 3 Storing Responses in Caches · 3.2 Updating Stored Header Fields · 3.5 Storing Responses to Authenticated Requests · 4 Constructing Responses from Caches · 4.1 Calculating Cache Keys with the Vary Header Field · 4.2 Freshness · 4.2.1 Calculating Freshness Lifetime · 4.2.3 Calculating Age · 4.3.3 Handling a Validation Response · 4.3.4 Freshening Stored Responses upon Validation · 4.4 Invalidating Stored Responses · 5.2.2.4 no-cache · 5.2.2.5 no-store · 5.2.2.7 private · 5.2.2.10 s-maxage
- MDN — ETag headerAvoiding mid-air collisions · Caching of unchanged resources
- MDN — If-None-Match headerIf-None-Match header · Directives
- MDN — If-Match headerIf-Match header