Blog post
One response, two very different caches
The browser in someone's hand and the Front Door point of presence both read Cache-Control, but they should not make the same decision. Our policy keeps entry pages fresh for people while still letting the shared edge do useful work.
Stable entry pages must be revalidated by browsers so they can discover each deployment's newly fingerprinted assets. Front Door may retain those same pages at the edge until the deploy job explicitly purges them. Fingerprinted files take the opposite deal: their unique names make them safe to hold for a long time, and deploys never purge them.
- Assets first Upload every long-cache object before a page can reference it.
- Queue before mutation Persist pending, current durable, and dropped unhashed paths under
state/before changing visitor files. - Entry layer last Then publish index pages and explicit stable extras under
public/;state/version.txtis workflow state outside the tenant hostname's routed subtree. - Purge precisely Invalidate the queued FQDN-relative paths—not fingerprints, with
/*only when deterministic compaction has no narrower answer. - Prove the result Sweep durable URLs in parallel: require 200 plus matching staged/fetched SHA-1 for current paths, and 404 for exact dropped paths. Production and preview have separate caches.
The contract on the wire
| Kind | Browser | Edge | Deploy purge |
|---|---|---|---|
Index pages and stable extras under public/ | max-age=0, must-revalidate | May retain for four weeks (s-maxage=2419200) until durable-path purge | Yes, for this hostname |
| Fingerprinted and dated assets | max-age=2592000, immutable (30 days) | Honor the origin header | No—the new deploy uses a new URL |
The entry layer must not send no-store from origin: Front Door then refuses to cache it, reported as PRIVATE_NOSTORE. The client-facing response must still contain max-age=0 even when the edge keeps its own copy. “The browser must revalidate” and “the point of presence may cache” are compatible statements.
Every hostname is its own cache
Production and preview do not share cached objects even when their public paths and bytes are identical. Changing a fingerprinted file's cache lifetime without changing its filename requires a one-time purge of that hashed path on each environment hostname—or waiting for the old lifetime to expire. A production-only purge cannot repair preview.
Why upload order matters
Queue durability comes before dropped durable blobs are deleted or any public object is uploaded. Fingerprinted assets then go first and stable HTML goes last. A visitor arriving in the middle therefore sees either old HTML with assets that still exist, or new HTML whose assets already exist. The deploy is complete only after each current durable body produces the same SHA-1 as its staged file, each dropped durable path returns 404, and the unchanged queue is removed; a sleep or a successful upload command is not equivalent evidence.
Failure remains visible
A failed purge or public verification leaves state/to-purge-YYYYMMDD.txt for the next deploy to union and retry. Its date is the original unfinished date, not today’s retry date. Lifecycle cleanup never sweeps queue files; one older than about 30 days is a warning that needs attention.
Continue to the service that performs the purge, or return to all posts.