Skip to content
← Free FDE training guides

Free learning / 45-minute HTTP cache proof lab

HTTP caching: select the right copy before reusing it

Run actual local HTTP requests to expose a wrong-language fresh hit, validate a stored body with 304 and distinguish no-cache, no-store and Vary star.

No account needed. Use your own copy for writing and keep the original as a reference.

Engineering lab library / Run locally

Choose your next agent lab.

Start with the durable workflow reference, then choose the boundary you want to test. Open its walkthrough for setup, commands and expected observations.

Synthetic records. No provider key or model inference. Runs locally with the standard library.

  1. Start here / Python 3.11+ · Standard library

    HTTP variants, revalidation and retention

    Expose a wrong-language hit, inspect real 304/200 exchanges and withhold an orphan validation response.

    Python 3.11+ · Standard library · Owned loopback HTTP · No outside requests

Original LockedIn Labs workshop · 45 minutes. Python 3.11+ standard library. Read a request, a response status and JSON first. The runner starts its own short-lived HTTP origin on a random 127.0.0.1 port, sends actual GETs and closes it. No packages, account, credentials, outside requests or database are used. All instruction text and source revisions are synthetic.

You will distinguish selecting a representation, retaining its bytes and validating a later use. The Caching 101 workshop covers application keys, current authority, expiry and late publication. This workshop adds HTTP evidence; it does not replace those application checks.

1. Separate the address from the representation

A representation is the particular response content selected for a resource. Our /localized address can return English or French. Accept-Language is the request header selecting exactly en or fr here; language preference lists, quality values and regional negotiation are deliberately outside the fixture. The origin responds with Vary: Accept-Language, identifying that selecting request field. A URL-only key can therefore find the wrong saved copy.

Cache-Control describes response caching conditions. The unqualified no-cache requires validation before later reuse; no-store prohibits cache retention. Vary: * prevents a reusable request match. These are separate rules, not alternative spellings of the same instruction. Our small client elects not to retain star responses. HTTP Caching, RFC 9111 is the primary source for these distinctions.

An ETag is an opaque representation validator. Our origin invents tags such as "en-r1"; a real client copies its service's tag instead of guessing it. If-None-Match carries that validator on a conditional GET. A 304 Not Modified response carries no new representation body; it can validate saved bytes rather than supply a replacement document. HTTP Semantics, RFC 9110 defines this response. It differs from a new 200 response containing JSON.

2. Follow the selected body across the boundary

Predict two things separately: whether a request reaches the origin, and which representation the client returns. A cache result is not necessarily an HTTP response that just crossed the network. In the report, network_status: null means this request used a local fresh entry. It does not mean the origin returned a missing or invented status.

The visual compares six independently reset cases from the runner. It is an authored explanation; running the Python file supplies your own HTTP evidence.

UNDERSTAND THE SYSTEM

A stored response must fit this request

Six authored frames explain actual loopback requests from the downloadable Python lab. The diagram does not execute a cache in your browser. Keep origin responses, stored bodies and the result returned to this caller separate.

FOLLOW THE WORK

Watch one example, step by step.

6 authored stepsYou control the pace
01 / EXPLORE THE PARTSControls loading · written explanations below
Direction of information or workReturn / feedback

WHAT THIS PART DOES

Current request

The path is not the complete representation choice. In this fixture Accept-Language is exactly en or fr. No cookie, credential, tenant or browser cache is involved.

CONNECTED TO
  • Select: Stored candidate
Read the full worked example
  1. 01
    1 · A fresh hit can be the wrong variant

    The URL-only case fetches English, then returns that saved English body to a French request without another origin call. Its age is acceptable; its request match is not. The origin really supplied Vary: Accept-Language.

    BROKEN SELECTION: Request fr · returned en · origin calls 1

  2. 02
    2 · Keep the selecting fields with the body

    The guarded case fetches English and French independently, then reuses English for the third request. Inspect two stored entries with their original language headers. The result is two origin calls, not three and not one.

    VARIANT-AWARE SELECTION: Requests en / fr / en · origin calls 2

  3. 03
    3 · No-cache still permits a stored body

    The validate route returns no-cache, max-age=600. Its second request must reach the origin. If-None-Match matches the saved validator, so HTTP 304 transfers zero body bytes and the client returns its stored revision 1. A network call still happened.

    ACTUAL CONDITIONAL GET: 200 then 304 · stored body returned

  4. 04
    4 · A changed representation needs new bytes

    After the second validation, the fixture driver advances the origin to revision 2. The third conditional request still presents r1. The origin returns 200 with r2 bytes, and the matching cache entry is replaced. The second request's 304 did not promise future immutability.

    ACTUAL SOURCE CHANGE: 200 / 304 / 200 · returned revision 2

  5. 05
    5 · Do not confuse retention with validation

    No-store and Vary-star each make two origin requests in separately reset cases, with zero retained entries under this fixture's conservative policy. No-store forbids cache retention; star prevents a reusable request match. The reasons differ, even when these observed counts agree.

    TWO DISTINCT CASES: 200 / 200 each · retained entries 0

  6. 06
    6 · A 304 cannot invent a missing body

    The orphan case deliberately injects a faulty upstream 304 into an empty cache, without a conditional request. The client records the real 304 and zero transferred body bytes, then withholds because no matching representation exists. It does not turn absence into an empty successful document.

    FAULTY UPSTREAM RESPONSE: 304 · no entry · result withheld

Read the connections in plain English
  1. 01
    Current request → Stored candidate

    Use current request fields, not just the resource address.

  2. 02
    Representation bytes → Stored candidate

    Retain the original selecting header values beside each saved body.

  3. 03
    Stored candidate → Owned HTTP origin

    A missing variant or no-cache requirement reaches the owned origin.

  4. 04
    Owned HTTP origin → Reuse decision

    Separate a complete 200 representation from a bodyless 304 response.

  5. 05
    Reuse decision → Representation bytes

    This fixture stores eligible 200 bodies and refreshes matching validated entries.

  6. 06
    Representation bytes → Reuse decision

    A validator cannot supply bytes that were never stored.

  7. 07
    Reuse decision → Receiving evidence

    Record the actual representation or an explicit unresolved result.

Check your understanding: Does no-cache mean nothing can be stored, or that a repeat avoids the network?

Neither. In this authored case, the body is stored, but reuse requires validation. The second call receives an actual 304 with no body, then returns the matching saved bytes. No-store is a different retention rule. Neither header is application authorization or proof of overall privacy.

3. Run six actual HTTP comparisons

Download and read the runner. Keep an untouched reference and write predictions before opening its JSON output:

Code example
python3 --version
python3 fde-http-cache.py > cache-original.json
python3 fde-http-cache.py --case url-only-variant --first-language fr > cache-reversed.json

Each case has its own origin and empty cache. The driver performs requests in a deliberate sequential order; this is not concurrent traffic, a CDN or a browser cache test. Successful completion records listener_closed: true only after the owned server thread stops and its listener socket closes.

CaseNetwork status sequenceActual result to explain
url-only-variant200, local reuse, local reuseen / fr / en requests all receive English; only one origin request
vary-aware200, 200, local reuseen / fr / en receive their respective language; two stored entries
no-cache-revalidation200, 304, 200Stored r1 reused after validation, then replaced with r2 bytes
no-store200, 200Both requests reach origin; no reusable entry retained
vary-star200, 200No request match; this client retains no entry
orphan-304304Deliberately faulty upstream response; result withheld with no body invented

Open the written ledger after inspecting the diagram:

Text example
url-only-variant, request 2:
  requested_language: fr
  source: stored-fresh
  network_status: null
  representation.language: en
  origin_exchanges count: 1

vary-aware, request 2:
  requested_language: fr
  source: origin-body
  network_status: 200
  representation.language: fr
  origin_exchanges count: 2 after all three requests

no-cache-revalidation, request 2:
  If-None-Match: "en-r1"
  origin response: 304, body_bytes: 0
  source: validated-stored
  representation: saved English revision 1

no-cache-revalidation, request 3:
  If-None-Match: "en-r1"
  origin response: 200, ETag: "en-r2", new JSON bytes
  source: origin-body
  representation: English revision 2

Read origin_exchanges beside steps, not just a hit count. The origin actually received a conditional GET in the 304 step. Zero response-body bytes is not zero network traffic, zero header bytes, a measured latency gain or an avoided model call. The fixture doesn't use a model.

In stored_entries, compare each body's language with its saved request_headers. The guarded selection keeps both together. The deliberately broken selection ignores only Vary; it still checks age and no-cache. A young entry can therefore be the wrong representation without being expired.

4. Change the first request without changing its meaning

Compare cache-original.json with cache-reversed.json. The latter starts in French, requests English next, then French again. The URL-only cache now returns French to the English request. Reversing the order changes the wrong answer, not the violated rule. The requested language remains the independent target.

Run the guarded counterpart with the same changed input:

Code example
python3 fde-http-cache.py --case vary-aware --first-language fr > cache-guarded-fr.json
python3 fde-http-cache.py --case no-cache-revalidation --first-language fr > cache-validated-fr.json

Both selected variants still match the requests. In the validation case, tags are now "fr-r1" and "fr-r2". Never compare a tag's apparent digits as a universal ordering rule. The origin owns the validator and its selected content. The driver advances the source only after the second request; no HTTP request from the learner mutates an outside service.

5. Diagnose before changing cache policy

The validate route returns no-cache, max-age=600. Read the client's no-cache branch before its age comparison. The second request validates despite being young. The 304 has the same authored ETag, Vary and Cache-Control; the client keeps the old body and refreshes its age record. The third request reaches the origin again and receives new bytes after the source revision changes.

The no-store route also advertises max-age, but the client stores no reusable entry. The report still shows the synthetic content delivered to its caller. No-store is not a promise that application reports, screenshots, logs or copies are erased. Do not put private records in this exercise.

The star case also retains zero entries, by this client's conservative choice. Do not conclude that the standard universally forbids storing a Vary-star response: its important reuse boundary is the absence of a matching request.

The orphan case intentionally violates the expected upstream exchange: it returns 304 to an unconditional request when no body has been stored. The client returns representation: null with reason no_matching_stored_representation. A receiver should investigate that exchange rather than treat an empty document as a successful response. The fixture withholds on other unsupported statuses.

Read LocalCache.request. Its supported scope is deliberately narrow: GET, these fixed paths/directives, one exact language header, complete JSON bodies, and unchanged validation metadata. It estimates local age conservatively from Date, observed response delay and monotonic residence; it never sleeps for a TTL experiment. It implements no general cache parser, shared authorization, cookies, TLS, partial responses, redirects, stale-on-error or browser service worker. Real 304 metadata updates and other HTTP cases need a complete cache implementation and receiving-environment tests. Use maintained infrastructure for production; do not deploy Python's teaching server or this small cache.

6. Receive the cache contract as a delivery pod

Use three roles, rotating the receiver on another run:

  • Builder: retain command, runtime, request headers, network observations, returned representation and source-change ordering. Show the exact broken rule.
  • Receiver: reverse the first language without author takeover. Locate the wrong copy, both guarded entries, and a bodyless 304 with its matching body.
  • Application/platform owner: name who owns representation selection, intermediary configuration, current authorization and unsupported responses. Keep an unresolved receiving-environment experiment with a bounded stop rule.

Each person keeps an individual record; a shared correct explanation cannot establish every participant's contribution:

Text example
Learner / run / exact command:
Prediction before execution:
Requested language / returned language / source:
Original request headers and origin exchange reference:
Observed status / validator / network body bytes:
Stored entry and original selecting fields:
My contribution / assistance received:
Changed input / prediction before rerun / observed evidence:
Receiving action actually attempted / assistance:
Unresolved behavior / accountable owner / next bounded experiment:

For an enterprise document API, identify the actual browser, proxy, gateway and application cache boundaries. Use synthetic content and an approved receiving route. Capture actual response headers and chosen representation through that route, then test changed preferences, source revisions and unavailable origin behavior. Language matching establishes neither tenant isolation nor current access. A provider's private/public setting is not a substitute for the application's resource authorization contract.

7. Defend it in an interview

Explain: Can a fresh hit be wrong? Use request 2 in the broken variant case, including requested and returned language and the absent second origin request.

Debug: Why does no-cache return stored bytes after a 304? Locate the exact conditional request, zero new body bytes and the retained r1 representation. Then explain why the third call receives r2 bytes.

Design: Specify a cache contract for a scoped enterprise source. Name the cached object, selecting fields, validation/retention policy, actual intermediary, authorization owner, unsupported-response behavior and receiving evidence. State what this local experiment leaves untested before recommending a rollout.

Primary references checked 28 October 2026: HTTP Caching, HTTP Semantics, and Python HTTP client. The runner, comparisons, diagram, interview prompts and receiving exercise are original instruction derived from the observed fixture behavior.

Before you move on

Keep the evidence of your work.

  • Original and reversed-language request/representation evidence.
  • A conditional GET and its matching stored body, kept separate.
  • An individual receiving record with application and platform responsibilities.

Keep these in your own working copy. Reading this page does not save work or record completion.

Suggested next free session

Caching 101: when can you reuse earlier work?

Connect HTTP reuse to current application authority and late publication. This is a suggested sequence; choose the practice that addresses your current gap.

Open the next free guide

Free account

Give your practice a home.

Try a role exercise with account drafts and a downloadable worksheet. Keep the files you made in this guide in your own working copy.

Create a free learning account

Guided training · by application

Build toward a program.

Request structured lessons and a training seat. Scope, schedule, feedback arrangements and tuition are confirmed in writing before you commit.

Explore a training application
Have Academy access?

Use your existing training seat to open worked explanations and practice in the related lesson. Creating a free account does not grant a seat.

Open the related Academy lesson →
Back to top ↑