From bdd28992872e889cee3de5c37eac0b2e0adc7b5f Mon Sep 17 00:00:00 2001 From: Rob Jacques <2531474+robbyrob42@users.noreply.github.com> Date: Thu, 3 Sep 2026 16:09:43 -0700 Subject: [PATCH] fix(forensics): browser navigations to /scope and /api/intake 404'd Cloudflare's asset router applies `not_found_handling` only to requests carrying `Sec-Fetch-Mode: navigate`. A normal request that misses the asset manifest is passed to the Worker; a navigation that misses is answered with 404.html and the Worker is never invoked. `/scope` and `/api/intake` are the only routes here rendered on demand, so neither is in the manifest. Every browser navigation to the scoping form got the 404 page, and because a form POST is also a navigation, the intake endpoint was unreachable from a browser at all. The revenue path was dead from the moment the property became reachable. Introduced with the 404 page itself. sites/www carries the same `not_found_handling` and is unaffected only because it ships no 404.html for the router to serve, so its misses still fall through to the Worker and its /contact form works. A comment there records the trap, since adding a 404 page to that site would break it the same way. Nothing caught it. The property had no DNS record until today, so nothing could navigate to it, and every check run against it used curl, which sends no Sec-Fetch-Mode and therefore always took the Worker path and always returned 200. `run_worker_first` lists the two on-demand routes rather than being set to `true`, so the Worker runs only for those and the static files are still served by the asset router without invoking it. Verified locally against `wrangler dev` with `Sec-Fetch-Mode: navigate`: /scope and /scope/ return 200, a POST to /api/intake returns 303 to `?error=captcha` rather than 404, static routes still 200, and an unknown path still returns the real 404 page. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01J53xszP5CdZLXfhRJWsSyv --- sites/forensics/wrangler.jsonc | 23 ++++++++++++++++++++++- sites/www/wrangler.jsonc | 6 ++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/sites/forensics/wrangler.jsonc b/sites/forensics/wrangler.jsonc index 17142fd..4f3af2b 100644 --- a/sites/forensics/wrangler.jsonc +++ b/sites/forensics/wrangler.jsonc @@ -18,7 +18,28 @@ "assets": { "directory": "./dist/client", "binding": "ASSETS", - "not_found_handling": "404-page" + "not_found_handling": "404-page", + // Without this, every browser navigation to a route that is not a built + // file 404s, and the Worker is never invoked at all. + // + // Cloudflare's asset router applies `not_found_handling` only to requests + // carrying `Sec-Fetch-Mode: navigate`. A request that misses the asset + // manifest is normally passed to the Worker -- but a *navigation* that + // misses is answered with 404.html instead. `/scope` and `/api/intake` are + // the only routes here that are rendered on demand, so they exist in no + // manifest, and a browser asking for either got the 404 page. A form POST + // is a navigation too, so intake was unreachable from a browser entirely. + // + // This was introduced with the 404 page itself. sites/www has the same + // `not_found_handling` and is unaffected only because it ships no 404.html + // for the router to serve, so its misses still fall through to the Worker. + // Nothing caught it because the property had no DNS record until it went + // live, and curl sends no Sec-Fetch-Mode -- every check passed. + // + // Listing routes rather than `true`: the Worker then runs only for the two + // paths that need it, and the other 40-odd static files keep being served + // by the asset router without invoking it. + "run_worker_first": ["/scope", "/scope/", "/api/*"] }, "observability": { "enabled": true }, "workers_dev": true, diff --git a/sites/www/wrangler.jsonc b/sites/www/wrangler.jsonc index 64d47c5..bd8375f 100644 --- a/sites/www/wrangler.jsonc +++ b/sites/www/wrangler.jsonc @@ -21,6 +21,12 @@ "assets": { "directory": "./dist/client", "binding": "ASSETS", + // Note, if a 404.html is ever added to this site: the asset router answers + // navigations that miss the manifest with that file instead of invoking + // the Worker, which would break /contact and /api/contact the way it broke + // the forensics property on 2026-09-03. The fix there is + // `run_worker_first`, listing the on-demand routes. This site is safe today + // only because it ships no 404.html. "not_found_handling": "404-page" }, "observability": { "enabled": true },