Hacker DirectoryHacker Directory

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.

CORE · RED · BLUE · ARTIFACT · PRACTICE

1. Core · Mechanism

Read every scenario without motion

304, body retained

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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

  1. 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
  2. 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
  3. 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
  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
  5. 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
  6. 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

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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

  1. 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
  2. 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
  3. 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
  4. 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
  5. 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
  6. 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?

  1. Yes; the precondition is only advisory because normal checks passed.
  2. 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
  1. The stored body is retained; 304 updates metadata on the matching entry.
  2. 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