Plate № 06 · Cloudflare gotchas
A pattern from the gf.cx specimen book
Workers-with-Assets · run_worker_first
"Default routes browser navigations through the assets-layer FIRST, bypassing the Worker. curl works, browser 404s. Should bake into gfcx_subdomain_new.py template."
The problem
A Cloudflare Pages project with a _worker.ts + assets binding looks like it should serve dynamic routes from the Worker and static files from the asset layer. In practice, browser navigations (requests with Sec-Fetch-Mode: navigate) route through the asset layer first, returning a 404 for routes the Worker would have handled. The symptom: curl returns 200, the browser returns 404. Easy to misdiagnose as DNS, cache, or routing.
The fix
# wrangler.toml — every new gf.cx subdomain serving both static + API
[assets]
directory = "./dist"
binding = "ASSETS"
run_worker_first = true # ← Worker gets first crack at every request
Where it applies
- Every gf.cx subdomain that serves both static pages and Worker-handled routes (auth, OAuth callbacks, JSON API, dynamic HTML)
- Pages projects migrating from a pure-static
pages_build_output_dirsetup to Workers-with-Assets (sandbox.gf.cx's migration on 2026-05-27 surfaced this exact trap) - Default to bake into
~/bin/gfcx_subdomain_new.pyso new subdomains start correct
How to recognise
If curl https://name.gf.cx/dynamic-route returns 200 and the same URL in a browser returns 404, with the response showing cf-cache-status: MISS or no Worker invocation log: this is the bug.
Reusable elements
The named, copyable pieces — lift any one without the others:
run_worker_first = truein the[assets]block ofwrangler.toml— the single line that makes the Worker authoritative for every request before the asset layer gets a look.cf-cache-statusHIT/MISS signal — response header that reveals whether the asset layer was hit (and the Worker bypassed). MISS with a 404 = asset layer answered; MISS with a 200 from the Worker = correctly wired. The diagnostic tell.gfcx_subdomain_new.pydefault — bakerun_worker_first = trueinto the scaffold so new subdomains start correct; don't leave it as a post-deploy discovery.
Reference
- Source
- Dan, 2026-05-28.
- In use / example
- sandbox.gf.cx — its Workers-with-Assets migration on 2026-05-27 surfaced this exact trap a second time (the first hit was
dare.co.uk/ip), proving the pattern travels across surfaces. - Reusable elements
run_worker_first = trueconfig ·cf-cache-statusdiagnostic header · scaffold default ingfcx_subdomain_new.py(listed above).- Build cost
- 90 minutes on
dare.co.uk/ipbefore thecf-cache-statusHIT/MISS signal broke the diagnosis open. Thecurl-works-browser-404 symptom is easy to misread as DNS, cache, or routing until you see that header.