Hacker DirectoryHacker Directory

WEB / WEB-09.02

Browser and shared HTTP caches

A cache decides when to reuse a copy as well as whether to store it. Compare browser and shared caches using one response, explicit clocks, and separate policies.

CORE · RED · BLUE · ARTIFACT · PRACTICE

1. Core · Mechanism

Read every scenario without motion

Shared hit

  1. Repeat GET

    Duru requests the same public target; the shared entry is supplied complete with matching Vary context.

    Browser / Duru
    GET ready
    Shared cache
    Copy stored; Age at receive12
    Origin / Atlas
    No new request
    Browser HTTP cache
    Same policy; lifetime60
  2. Request arrived

    The GET arrives at the shared cache; the origin has not seen this request.

    Browser / Duru
    Waiting response
    Shared cache
    GET received; copy candidate
    Origin / Atlas
    No new request
    Browser HTTP cache
    Same policy; lifetime60
  3. Calculate current age

    The UTC fixture gives initial14 plus resident10 equals age24, fresh against shared lifetime30.

    Browser / Duru
    Waiting response
    Shared cache
    Fresh: age24 < lifetime30
    Origin / Atlas
    No new request
    Browser HTTP cache
    Same age24 < lifetime60
  4. Cached 200 arrived

    The shared cache sends the stored body with Age24; this exchange has no origin round trip.

    Browser / Duru
    200 received; Çay; Age24
    Shared cache
    Served stored body; Age24
    Origin / Atlas
    No new request
    Browser HTTP cache
    Copy eligible; lifetime60
  5. Select boundary time

    At now26 resident16 produces age30; advancing a local clock is not a wire packet.

    Browser / Duru
    Last received Age24
    Shared cache
    age30 = shared lifetime30
    Origin / Atlas
    No new request
    Browser HTTP cache
    age30 < private lifetime60
  6. Separate reuse decision

    The shared entry is no longer fresh and s-maxage requires validation; the browser copy can remain fresh.

    Browser / Duru
    Displayed copy unchanged
    Shared cache
    Next shared reuse needs validation
    Origin / Atlas
    No new request
    Browser HTTP cache
    Private copy still fresh

Private copy

  1. Select private response

    In this separate exchange the origin will return private,max-age60; no new copy exists initially.

    Browser / Duru
    GET ready
    Shared cache
    Empty for this exchange
    Origin / Atlas
    private response policy selected
    Browser HTTP cache
    Empty
  2. GET reached cache

    The shared cache receives the request; no usable stored response exists and origin forwarding is prepared.

    Browser / Duru
    Waiting response
    Shared cache
    GET received; miss
    Origin / Atlas
    No request yet
    Browser HTTP cache
    Empty
  3. GET reached origin

    The forwarded request reaches the origin; private response policy is selected but has not reached the client.

    Browser / Duru
    Waiting response
    Shared cache
    Waiting origin
    Origin / Atlas
    GET received; 200 private ready
    Browser HTTP cache
    Empty
  4. Private 200 arrived

    The shared cache receives the response and does not store the new body because of unqualified private.

    Browser / Duru
    Waiting response
    Shared cache
    200 received; shared store=false
    Origin / Atlas
    200 private ready
    Browser HTTP cache
    Empty
  5. Reached browser

    The private response reaches the browser; its private-cache storage decision can now occur locally.

    Browser / Duru
    200 received; Çay
    Shared cache
    Forwarded; shared store=false
    Origin / Atlas
    200 private ready
    Browser HTTP cache
    Not yet stored
  6. Local private store

    The browser cache stores the private copy because the other supplied eligibility rules are satisfied.

    Browser / Duru
    200 received; Çay
    Shared cache
    No copy stored
    Origin / Atlas
    Exchange complete
    Browser HTTP cache
    Private copy stored; lifetime60

Validation required

  1. Fresh no-cache copy

    The browser holds a no-cache,max-age60 copy; age24 is fresh but successful validation has not occurred.

    Browser / Duru
    GET intent
    Shared cache
    Not on selected direct path
    Origin / Atlas
    No request yet
    Browser HTTP cache
    Stored; age24; validated=false
  2. Local reuse gate

    Unqualified no-cache stops reuse; storage and reuse are different gates.

    Browser / Duru
    Validation request prepared
    Shared cache
    Not on selected direct path
    Origin / Atlas
    No request yet
    Browser HTTP cache
    Stored but cannot reuse
  3. Validation arrived

    The browser sends a conditional GET to the origin; matching context and validator are supplied.

    Browser / Duru
    Waiting validation
    Shared cache
    Not on selected direct path
    Origin / Atlas
    Conditional GET received
    Browser HTTP cache
    Stored but cannot reuse
  4. Select changed result

    The origin selects a changed representation and prepares a full 200 instead of validating the old body.

    Browser / Duru
    Waiting validation
    Shared cache
    Not on selected direct path
    Origin / Atlas
    200 full replacement ready
    Browser HTTP cache
    Stored old copy
  5. Full response arrived

    New 200 content reaches the browser; WEB-09.03 separately shows body retention after 304.

    Browser / Duru
    New 200 received; Atlas note
    Shared cache
    Not on selected direct path
    Origin / Atlas
    200 full replacement ready
    Browser HTTP cache
    Stored old copy
  6. Local replacement

    The new eligible response replaces the old copy; if it is no-cache, the next reuse again needs validation.

    Browser / Duru
    Use newly received body
    Shared cache
    Not on selected direct path
    Origin / Atlas
    Exchange complete
    Browser HTTP cache
    New copy stored; next reuse validates

No-store response

  1. Policy change

    This exchange fetches a new origin response separately from an older entry; no purge guarantee is supplied.

    Browser / Duru
    New GET ready
    Shared cache
    Old entry retained separately
    Origin / Atlas
    No request yet
    Browser HTTP cache
    Old entry retained separately
  2. GET reached origin

    The direct GET reaches the origin; no-store is selected for the new response rather than a retroactive store operation.

    Browser / Duru
    Waiting response
    Shared cache
    Old entry retained separately
    Origin / Atlas
    GET received; no-store policy
    Browser HTTP cache
    Old entry retained separately
  3. New 200 prepared

    The origin prepares harmless new content with no-store; cache policy does not imply encryption.

    Browser / Duru
    Waiting response
    Shared cache
    Old entry retained separately
    Origin / Atlas
    200 no-store ready
    Browser HTTP cache
    Old entry retained separately
  4. No-store arrived

    The browser receives the response; caching and future reuse of this immediate response are prohibited.

    Browser / Duru
    200 no-store received
    Shared cache
    Old entry retained separately
    Origin / Atlas
    200 no-store ready
    Browser HTTP cache
    Old entry retained separately
  5. Do not store new copy

    The browser does not write the new response into its cache; the earlier entry is not marked automatically erased.

    Browser / Duru
    Use immediate body
    Shared cache
    Old entry retained separately
    Origin / Atlas
    Exchange complete
    Browser HTTP cache
    New store=false; old entry retained
  6. Separate earlier state

    The fixture retains the earlier ledger entry; later use of an old copy requires a separate eligibility decision.

    Browser / Duru
    New response not reusable
    Shared cache
    No retroactive purge guarantee
    Origin / Atlas
    Exchange complete
    Browser HTTP cache
    No retroactive purge guarantee

ACTORS · STATE · DATA FLOW

Who is the copy stored for?

Duru opens the same public tea page again. A browser HTTP cache can be a private cache for one user, while a shared proxy or CDN serves multiple clients. Service Worker Cache Storage is a separate API/store; this lesson examines HTTP-cache decisions. Shared does not mean every representation is suitable for every user.

Storage eligibility and reuse are separate gates. Method/status, target, and any request fields selected by Vary must be appropriate. A stored response can still be unusable because of policy or freshness. The fixture selects a public GET with a complete body and matching Vary: Accept context rather than authenticated or partial content.

A cache response does not mean the origin saw the current request. In a fresh shared hit, the request reaches the proxy and a stored response goes to Duru. A fresh browser copy can avoid an external request entirely. Drawing local lookup as a network hop would hide the benefit caching provides.

For HTTPS, a shared cache needs a trusted endpoint/termination arrangement that lets it see HTTP content. A proxy carrying only an opaque TLS tunnel cannot read the encrypted response body and make this cache decision. The Atlas scene supplies an authorized shared endpoint observing decoded HTTP messages; it does not model TLS termination or certificate validation.

Where does the age clock start?

The artifact has Date 00:00:00, request_time 00:00:08, response_time 00:00:10, and received Age 12 seconds on the same synthetic UTC day. apparent_age=max(0,10−0)=10, response_delay=10−8=2, and corrected_age_value=12+2=14. This fixture selects conservative corrected_initial_age=max(10,14)=14. The RFC also permits corrected_age_value itself; the max form is not presented as the only mandatory formula for every implementation.

At now=00:00:20, resident_time=20−10=10 and current_age=14+10=24. Using received Age 12 alone omits transit and local residence. Response Date is not the business object’s modification time. Clocks are supplied and synchronized here; real drift and resolution are separate limitations.

With Cache-Control: public, max-age=60, s-maxage=30, private/browser lifetime is 60 and shared lifetime is 30. A response is fresh when its freshness lifetime exceeds current_age. At now=00:00:26, age is 30: the shared entry is no longer fresh while the browser entry still is. s-maxage overrides max-age/Expires for shared caches and requires validation before stale reuse. No stale-serving extension is selected here.

Three directives make different decisions

Unqualified private prevents a shared cache from storing the response; a private browser cache can store it if other rules allow. It is neither content encryption nor session isolation. Unqualified no-cache permits storage but requires forwarding to the origin and successful validation before reuse, even when age is below the freshness lifetime.

no-store requires that the immediate request/response not be stored by caches or reused for later requests. It is not a command to retroactively delete a previously stored response for the URL. A policy change needs a separate invalidation/validation strategy for old copies. The fixture keeps an older entry separately; the new no-store response is not written as its replacement.

The no-cache and private directives here have no field-name arguments; qualified forms have narrower rules and are outside this example. Connect directives to who stores, who reuses, and when validation occurs. Do not claim an HTTP-cache model measures all browser-history or back/forward-cache behavior.

2. Red · Trust & Impact

Read every scenario without motion

Storage scope

  1. The private directive was tied to cache type

    The private, max-age=60 row has sharedMayStore false and privateMayStore true. Storage in a private browser cache is not independently a violation. Private implies neither encryption nor user-identity proof.

    Cache policy ledger
    Private: shared store false / private store true
    Store/reuse gate
    Decision pending
    Recipient boundary
    No verdict
  2. Freshness did not repair storage eligibility

    A shared cache may not store this private response, even if its age is zero. Copying public Çay eligibility into a personalized response creates the wrong recipient boundary. Storage policy retains its predicate independently of age.

    Cache policy ledger
    Private: shared store false / private store true
    Store/reuse gate
    Private shared storage disallowed regardless of age
    Recipient boundary
    No verdict
  3. Cache classification was not permission

    Freshness establishes neither authentication, object permission nor correct representation selection. Application-account state changes do not automatically repair every cache; affected representations need explicit handling.

    Cache policy ledger
    Private: shared store false / private store true
    Store/reuse gate
    Private shared storage disallowed regardless of age
    Recipient boundary
    Fresh ≠ authorized recipient
  4. No-store was read as policy for the new response

    The no-store row disallows shared and private storage of the new response. retroactiveEraseGuaranteed is false and priorEntryInLedgerRetained true. The directive is not a command that deletes every previous copy.

    Cache policy ledger
    No-store: new storage false / old entry retained
    Store/reuse gate
    Private shared storage disallowed regardless of age
    Recipient boundary
    Fresh ≠ authorized recipient
  5. Purging was separated from storage control

    reuseThisResponsePermitted is false for this response. Retention of an older entry is a separate policy and cache-behavior question. no-store does not establish automatic erasure of all browser history or Service Worker state.

    Cache policy ledger
    No-store: new storage false / old entry retained
    Store/reuse gate
    New response not reused / purge separate
    Recipient boundary
    Fresh ≠ authorized recipient
  6. Storage review retained its risk boundary

    This inert ledger distinguishes policy meanings without cross-user serving or a real browser cache. Confidentiality review needs recipient, response classification and actual store/reuse evidence. A directive name alone does not verify the full boundary.

    Cache policy ledger
    No-store: new storage false / old entry retained
    Store/reuse gate
    New response not reused / purge separate
    Recipient boundary
    Policy meaning known / actual confidentiality unmeasured

Validation gate

  1. No-cache separated storage from reuse

    Shared and private caches may store a no-cache, max-age=60 response. reuseRequiresValidation is true. Being within its freshness lifetime does not remove that requirement; no-cache does not mean never store.

    Cache policy ledger
    May store / validation required before reuse
    Store/reuse gate
    Decision pending
    Recipient boundary
    No verdict
  2. Reuse was withheld until validation completed

    With validationCompleteSupplied false, reusePermitted stays false. Freshness and validation are different gates. Storing a response does not establish successful consultation with the origin.

    Cache policy ledger
    May store / validation required before reuse
    Store/reuse gate
    Validation false / reuse false
    Recipient boundary
    No verdict
  3. Pending validation remained an ordinary condition

    This wait is neither malicious cache behavior nor application denial. Review must observe the origin round trip and policy decision separately. Timeout and stale serving are not enabled in this fixture.

    Cache policy ledger
    May store / validation required before reuse
    Store/reuse gate
    Validation false / reuse false
    Recipient boundary
    Validation pending / stale serving not enabled
  4. A changed representation entered the validation ledger

    The stored tag is atlas-v1 with body Çay. For If-None-Match, the origin supplies current atlas-v2 and Atlas note with status 200. The new response carries ten content bytes, rather than retaining the old body as with 304.

    Cache policy ledger
    Validation response 200 / atlas-v2 / Atlas note
    Store/reuse gate
    Validation false / reuse false
    Recipient boundary
    Validation pending / stale serving not enabled
  5. Replacement updated the tag and body together

    The ledger sets cacheAfterTag atlas-v2 and cacheAfterBody Atlas note. nextReuseRequiresValidation remains true; one successful validation does not permanently remove no-cache policy. Store update and later reuse remain separate decisions.

    Cache policy ledger
    Validation response 200 / atlas-v2 / Atlas note
    Store/reuse gate
    Cached v2/body replaced / next validation required
    Recipient boundary
    Validation pending / stale serving not enabled
  6. Normal validation was not application trust

    Store/reuse predicates are explicit for this public GET ledger. Authenticated caching, production permission and full dependency invalidation are unmeasured. Correct cache policy cannot repair wrong user/object authorization.

    Cache policy ledger
    Validation response 200 / atlas-v2 / Atlas note
    Store/reuse gate
    Cached v2/body replaced / next validation required
    Recipient boundary
    Validation semantics known / authz separate

ASSUMPTION · BOUNDARY · CONTROL

Fresh does not establish the correct user

An account summary can have been generated seconds ago and still violate policy if stored in a shared cache. Correct age arithmetic verifies neither identity, permissions, nor representation selection. The failure condition is classifying a personalized response like public tea and reducing reuse eligibility to time alone.

Potential effects include serving content to the wrong recipient or displaying a copy governed by an old policy. Review response classification, private/no-store requirements, and selected representation context together. Requests carrying Authorization have additional shared-reuse rules; the mere presence of credentials is not a universal never-cached guarantee.

Application transitions such as login/logout do not automatically correct every intermediary cache. A cache invalidates the target after receiving a non-error 2xx/3xx response to an unsafe method, but they do not imply clearing every dependent URL or browser display. The product contract must identify the affected representations.

Do not substitute policy names for each other

Interpreting no-cache as never store, private as encrypted, and no-store as delete history creates three different incorrect acceptance rules. Reusing a fresh no-cache entry without validation is wrong; a private entry in a private cache is not itself a violation. An older copy surviving a later no-store response illustrates the absence of a retroactive purge.

Controls require more than seeing response headers: observe storage decisions and reuse on a subsequent normal request. This lesson explains conditions, consequences, and expected controls using an inert policy ledger. It uses no personal data and provides no cache-poisoning, key-collision, or third-party targeting exercise.

3. Blue · Secure development & defense

Read every scenario without motion

Age ledger

  1. Three timestamps were separated from origin Date

    The example calculator produces the timing ledger; a synthetic observation fixture supplies validationRequired/validated/reused rows. These are not real cache logs. A public hit sends no request to the origin; origin evidence in private/no-cache branches exists only after that message arrives. A missing response or reuse record cannot prove correct behavior. Date is 00:00:00, request time 00:00:08 and response time 00:00:10. Received Age is 12 seconds. Response delay 2 and apparent age 10 are calculated independently; Age is not business-modification time.

    Time/policy record
    Apparent age 10 / response delay 2 / received Age 12
    Age/validation review
    Decision pending
    Evidence verdict
    No verdict
  2. Conservative initial age used the correct inputs

    Corrected age value is 12+2=14. The selected conservative rule max(10,14) yields initial age 14. This uses supplied clocks without measuring drift, origin-clock accuracy or actual cache transit.

    Time/policy record
    Apparent age 10 / response delay 2 / received Age 12
    Age/validation review
    Corrected initial age max(10,14)=14
    Evidence verdict
    No verdict
  3. Resident time was added separately to current age

    At now 00:00:20, resident time is 10 and currentAgeSeconds 24. The shared hit generates Age 24. Forwarding received Age 12 unchanged would ignore elapsed residence.

    Time/policy record
    Resident 10 / current age 24 / hit Age 24
    Age/validation review
    Corrected initial age max(10,14)=14
    Evidence verdict
    No verdict
  4. Freshness was compared with the cache-type lifetime

    The public policy supplies private lifetime 60 and shared lifetime 30. Age 24 is fresh in both. At later now 00:00:26, age is 30; equality makes sharedFresh false while privateFresh stays true.

    Time/policy record
    Resident 10 / current age 24 / hit Age 24
    Age/validation review
    At age30: shared stale / private fresh
    Evidence verdict
    No verdict
  5. Eligibility assumptions accompanied the freshness verdict

    Complete GET 200, matching target/Vary, absent Authorization and no stale extension are supplied. The calculation covers this context. Transferring fresh-hit results to authenticated requests or another representation exceeds evidence scope.

    Time/policy record
    Resident 10 / current age 24 / hit Age 24
    Age/validation review
    At age30: shared stale / private fresh
    Evidence verdict
    Age known under supplied eligibility
  6. Clock regression did not claim actual runtime behavior

    Timestamp and age arithmetic are reproducible without running a browser/proxy cache. Cache type, selecting fields and current age are reviewed together. One Age field yields no privacy or authorization verdict.

    Time/policy record
    Resident 10 / current age 24 / hit Age 24
    Age/validation review
    At age30: shared stale / private fresh
    Evidence verdict
    Arithmetic reproducible / runtime unmeasured

Validation gap

  1. The selector selected the required-validation decision

    The example calculator produces the timing ledger; a synthetic observation fixture supplies validationRequired/validated/reused rows. These are not real cache logs. A public hit sends no request to the origin; origin evidence in private/no-cache branches exists only after that message arrives. A missing response or reuse record cannot prove correct behavior. Normal XML reports validationRequired true, validated false and reused false. This authored observation agrees with the no-cache gate. The XML fields are not presented as a real cache-product log schema.

    Time/policy record
    Required=true / validated=false / reused=false
    Age/validation review
    Decision pending
    Evidence verdict
    No verdict
  2. Normal and positive controls tested one predicate

    The normal count is zero. Positive XML has validated false with reused true and produces one match. This tests the oracle’s ability to select a violation observation, not occurrence of a real cache violation.

    Time/policy record
    Required=true / validated=false / reused=false
    Age/validation review
    Normal 0 / positive control 1
    Evidence verdict
    No verdict
  3. A missing reuse event can yield zero matches

    The alternative review lacks a reuse-decision observation. The selector may find no existing inconsistent row. Copying reused false from the normal fixture into the missing event would conceal an actual coverage gap.

    Time/policy record
    Reuse observation absent
    Age/validation review
    Normal 0 / positive control 1
    Evidence verdict
    No verdict
  4. Storage evidence did not replace validation evidence

    An existing cache entry does not establish completed validation. Origin response, selected candidate and next-reuse policy need separate observation. The cause of pending validation cannot be inferred from a missing row alone.

    Time/policy record
    Reuse observation absent
    Age/validation review
    Stored entry ≠ validation completed
    Evidence verdict
    No verdict
  5. Reuse correctness remained unknown

    Reuse correctness remains UNKNOWN until validation and reuse coverage are complete. A missing event proves neither a clean cache nor malicious serving. Required policy and actual observations retain separate sources.

    Time/policy record
    Reuse observation absent
    Age/validation review
    Stored entry ≠ validation completed
    Evidence verdict
    UNKNOWN: validation/reuse coverage
  6. New evidence was bounded to minimal context

    Further review needs policy class, selected entry, validation outcome and reuse decision, not full content or credentials. Ordinary hits and origin round trips are evaluated as separate observations.

    Time/policy record
    Reuse observation absent
    Age/validation review
    Stored entry ≠ validation completed
    Evidence verdict
    Needed: policy/entry/validation/reuse scope

IMPLEMENTATION · SECURITY CONTROL · VERIFICATION

Recalculate the timing ledger

The example calculator produces the timing ledger; a synthetic observation fixture supplies validationRequired/validated/reused rows. These are not real cache logs. A public hit sends no request to the origin; origin evidence in private/no-cache branches exists only after that message arrives. A missing response or reuse record cannot prove correct behavior. Begin the manual exercise by finding Date, request_time, response_time, received Age, and now. Calculate apparent age and response delay independently and explain the selected conservative initial age of 14. Record resident_time and current_age at now 20 and 26 separately rather than copying a single Age header.

Select the cache type next: lifetime 30 for shared, 60 for browser. Compare the equality boundary where freshness becomes false. When serving a stored response without validation, a cache generates Age from current_age; Duru receives Age 24 in the scene. Forwarding the originally received Age 12 would not express the current observation.

The selector finds authored observations with validationRequired=true, validated=false, and reused=true. Normal and positive fixtures yield 0 and 1; a missing event returning 0 cannot verify reuse control. This XML is not a cache-product log schema. Actual decisions need request/response and timing context.

Define separate acceptance criteria for storage and reuse

For the public branch, expect an eligible fresh copy to be served without reaching the origin. For private, check shared store false and browser store true. In no-cache, note that storage is allowed but reuse requires completed origin validation. In no-store, the new response is not stored and there is no general purge guarantee for earlier entries.

Treat normal hits, origin round trips, and policy changes as separate observations. Serving stale while disconnected follows additional rules and is not enabled here. WEB-09.03 develops validators and how successful 304 validation retains a body. This policy ledger is not execution of an actual browser or proxy cache.

A handler should generate Cache-Control from the response’s audience and permitted use. Choose private when personalized content may be stored only by that client; a cookie does not automatically tell a shared cache to keep it private. no-store for no storage of the new response and no-cache for origin validation before each reuse are different decisions. Verify storage and reuse expectations separately instead of copying the public Çay policy onto personal content.

NOW APPLY IT TO THE CASE

What separate decisions arise from freshness, private/no-cache/no-store and validation?

The UTC ledger gives the Çay response an age of 24 seconds. Shared lifetime is 30 seconds and private lifetime 60; their outcomes diverge at age 30. Private storage, no-cache validation and no-store retention have different predicates.

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

Cache-policy and UTC-age ledger

Select a field and examine what it supports and where the evidence ends.

Read the raw artifact and field explanations
{
  "target": "https://library.atlas.example/tea?view=plain",
  "representation": {
    "content": "Çay",
    "contentOctets": 4,
    "vary": "Accept",
    "originalRequestAccept": "text/plain"
  },
  "clock": {
    "date": "2026-10-08T00:00:00Z",
    "requestTime": "2026-10-08T00:00:08Z",
    "responseTime": "2026-10-08T00:00:10Z",
    "receivedAgeSeconds": 12,
    "now": "2026-10-08T00:00:20Z",
    "apparentAgeSeconds": 10,
    "responseDelaySeconds": 2,
    "correctedAgeValueSeconds": 14,
    "selectedInitialAgeRule": "conservative max(apparent_age, corrected_age_value)",
    "correctedInitialAgeSeconds": 14,
    "residentTimeSeconds": 10,
    "currentAgeSeconds": 24
  },
  "publicPolicy": {
    "cacheControl": "public, max-age=60, s-maxage=30",
    "privateLifetimeSeconds": 60,
    "sharedLifetimeSeconds": 30,
    "sharedFreshNow": true,
    "privateFreshNow": true,
    "sharedHitGeneratedAge": 24,
    "laterNow": "2026-10-08T00:00:26Z",
    "laterResidentSeconds": 16,
    "laterCurrentAgeSeconds": 30,
    "laterSharedFresh": false,
    "laterPrivateFresh": true
  },
  "policies": [
    {
      "id": "private",
      "cacheControl": "private, max-age=60",
      "sharedMayStore": false,
      "privateMayStore": true,
      "reuseRequiresValidation": false
    },
    {
      "id": "no-cache",
      "cacheControl": "no-cache, max-age=60",
      "sharedMayStore": true,
      "privateMayStore": true,
      "reuseRequiresValidation": true,
      "validationCompleteSupplied": false,
      "reusePermitted": false
    },
    {
      "id": "no-store",
      "cacheControl": "no-store",
      "sharedMayStore": false,
      "privateMayStore": false,
      "reuseThisResponsePermitted": false,
      "retroactiveEraseGuaranteed": false,
      "priorEntryInLedgerRetained": true
    }
  ],
  "eligibilitySupplied": {
    "completeGet200": true,
    "targetAndVaryMatch": true,
    "requestAuthorizationAbsent": true,
    "noStaleExtensionEnabled": true,
    "noQualifiedFieldNameDirectives": true
  },
  "selector": {
    "language": "XPath",
    "xml": "<observations><reuse validationRequired=\"true\" validated=\"false\" reused=\"false\"/></observations>",
    "xpath": "count(/observations/reuse[@validationRequired=\"true\" and @validated=\"false\" and @reused=\"true\"])",
    "expected": 0,
    "positiveFixtureXml": "<observations><reuse validationRequired=\"true\" validated=\"false\" reused=\"true\"/></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."
  },
  "validationExchange": {
    "storedBeforeTag": "\"atlas-v1\"",
    "storedBeforeBody": "Çay",
    "requestHeader": {
      "If-None-Match": "\"atlas-v1\""
    },
    "originCurrentTagSupplied": "\"atlas-v2\"",
    "originCurrentBodySupplied": "Atlas note",
    "responseStatus": 200,
    "responseBody": "Atlas note",
    "responseContentOctets": 10,
    "responseCacheControl": "no-cache, max-age=60",
    "cacheAfterTag": "\"atlas-v2\"",
    "cacheAfterBody": "Atlas note",
    "nextReuseRequiresValidation": true
  }
}
clock
Response age from origin/cache transit and local residence, not business modification time.
publicPolicy
Private60/shared30 for the same age; equality makes shared freshness false.
policies
Separate storage, validation, and reuse decisions.
priorEntryInLedgerRetained
Instructional state that does not assume no-store automatically erases earlier entries.

Clocks, storage eligibility, and policy engine are authored/supplied. Browser history, Service Workers, actual proxies, drift, stale extensions, and authenticated caching are not measured.

Two reality checks

A response with no-cache cannot exist in a cache.

It can be stored; unqualified no-cache prevents subsequent reuse without successful origin validation.

no-store deletes every previous copy; Age is just the received header number.

no-store is not retroactive purge. current_age includes response delay and local residence in addition to received Age.

YOUR TURN

Work through this story.

Predict the outcome, find the evidence boundary, and explain it in your own words.

Current age is 30, shared lifetime 30 and private lifetime 60. Which cache is fresh?

  1. Shared is stale; private is fresh.
  2. Both are fresh; equality provides another second.
Read the explained answer

Shared is stale; private is fresh. — Shared age equals its lifetime and is not fresh. The selected private lifetime is not yet exhausted.

Why is reusePermitted=false although a no-cache response can be stored?

Selected lesson artifact fields; teaching example

policies[1]
{"id":"no-cache","cacheControl":"no-cache, max-age=60","sharedMayStore":true,"privateMayStore":true,"reuseRequiresValidation":true,"validationCompleteSupplied":false,"reusePermitted":false}
  1. no-cache and no-store are identical; storage is impossible.
  2. Successful validation has not yet been supplied.
Read the explained answer

Successful validation has not yet been supplied. — No-cache is not a storage prohibition; reuseRequiresValidation=true and validationCompleteSupplied=false close the gate.

Date apparent age 10, received Age 12, response delay 2 and residence 10. Why is current age 24; why is Age 12 alone insufficient?

Read the explained answer

Corrected age value is 12+2=14; the selected conservative max(10, 14)=14 is the initial age. Adding residence 10 yields 24. Treating an Age-header snapshot as current age omits elapsed cache residence. This supplied clock ledger is not an actual cache-timer measurement.

□ I included response delay and residence.

□ I did not treat the supplied-clock calculation as an actual runtime measurement.

Open the application console · request / response / browser
Also open request, URL, cookie and DOM examples

Sources and scope

  • 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 — HTTP cachingPrivate caches · Shared cache · Fresh and stale based on age · Provide up-to-date content every time · Validation