- commit
- 7e83e210ae270e106740f9368a24616b8a5c3039
- parent
- 3edf2f937f320b726efd71826491911a7be3b55e
- author
- Dami <dami.etoile@gmail.com>
| M | ARCHITECTURE.md | 11 | ++++++----- |
| M | SECURITY.md | 6 | ++++++ |
| M | worker/ | 5 | +++-- |
| M | worker/ | 5 | +++++ |
| M | worker/ | 6 | ++++++ |
@@ -59,12 +59,13 @@ require isolation must explicitly use their exact hostname.
5959 6060 Serving normally requires one D1 read for site state and two R2 reads for the6161 manifest and file. Public responses use `ETag` revalidation and a 60-second62-Workers Cache entry. Cache hits skip Worker execution and storage reads,63-although they still count as Worker requests.62+browser cache entry. Every Worker response also sends63+`Cloudflare-CDN-Cache-Control: no-store`: the exact custom domain and wildcard64+route currently share an unsafe hostname-agnostic edge cache key, so an edge65+entry must never be replayed on a sibling site origin.6466 65-Public responses carry a site cache tag. Pushes, reverts, deletion, TTL changes,66-and password-policy changes purge that tag. Protected responses and errors are67-never publicly cached.67+Public responses retain site cache tags and purge hooks for a future host-safe68+edge-cache design. Protected responses and errors are never cached.6869 6970 Site roots always use `/`. Directory paths redirect to the trailing-slash form7071 so relative asset paths resolve correctly, and explicit `index.html` paths@@ -61,6 +61,12 @@ mirrors the hostname shape at the edge. Worker validation remains the source of
6161 truth, so correctness does not depend on the WAF rule or paid regular-expression6262 support.6363 64+The Worker disables Cloudflare shared-edge caching on every response with65+`Cloudflare-CDN-Cache-Control: no-store`. This is a required origin-isolation66+control: the exact custom domain and wildcard Worker route can otherwise reuse67+one cached public response for the same path on another hostname. Public sites68+still advertise short browser caching and support `ETag` revalidation.69+6470 ## Notification pairing6571 6672 Pairing is closed by default. An authenticated owner opens a fixed 10-minute@@ -178,8 +178,9 @@ activity digest is sent only when probes occurred in the preceding 24 hours.
178178 179179 ### Operational checks after deployment180180 181-- **Workers Metrics:** request volume and errors; cache hits reduce execution182- and storage reads but still count as Worker requests.181+- **Workers Metrics:** request volume and errors. Shared-edge caching is182+ intentionally disabled because exact and wildcard hostnames must never share183+ an artifact response; budget for each public request to execute the Worker.183184 - **Workers Observability:** filter `event = "security.request"`; Free retains184185 Workers Logs for three days.185186 - **D1 Metrics → Row Metrics:** rows written is the main enumeration-telemetry@@ -33,6 +33,11 @@ export default {
3333 const controlOrigin = isControlOrigin(env, url);3434 const siteAddress = controlOrigin ? null : siteAddressFromUrl(env, url);3535 const finish = (response: Response): Response => {36+ // Cloudflare currently keys Worker response cache entries for the exact37+ // custom domain and its wildcard route without preserving the request38+ // hostname. Never let a public artifact response be replayed on a sibling39+ // site's origin. Browser caching remains controlled by Cache-Control.40+ response.headers.set("cloudflare-cdn-cache-control", "no-store");3641 ctx.waitUntil(logSecurityRequest(req, env, url, response, siteAddress));3742 ctx.waitUntil(3843 recordEnumerationProbe(req, env, url, response, siteAddress),@@ -14,6 +14,9 @@ describe("Worker runtime", () => {
1414 1515 expect(response.status).toBe(200);1616 expect(response.headers.get("cache-control")).toBe("public, max-age=3600");17+ expect(response.headers.get("cloudflare-cdn-cache-control")).toBe(18+ "no-store",19+ );1720 expect(response.headers.get("content-security-policy")).toBe(1821 "frame-ancestors 'none'",1922 );@@ -295,6 +298,9 @@ describe("Worker runtime", () => {
295298 headers: { cookie: "nzip_aeffe=not-a-valid-unlock" },296299 });297300 expect(isolated.status).toBe(401);301+ expect(isolated.headers.get("cloudflare-cdn-cache-control")).toBe(302+ "no-store",303+ );298304 expect(isolated.headers.get("permissions-policy")).toBe(299305 "document-domain=()",300306 );