Hosting Notes: Cloudflare Pages, Workers, and the Day They Fought Each Other
What I learned deploying a Next.js static site to Cloudflare Pages — and why a forgotten Worker silently ate a whole week of deploys.
Short version of this post: if you deploy a Pages site and its custom domain stubbornly refuses to update, a Worker is probably intercepting the hostname. The DNS record can point wherever it wants; Workers win.
The setup
dashboard.e4z.xyz is a Next.js 16 static export deployed to a Cloudflare Pages project called e4z-xyz. Routing looks like this on paper:
Browser → Cloudflare DNS (proxied) → Pages project → ./out
DNS is a proxied CNAME from dashboard.e4z.xyz to e4z-xyz.pages.dev. Simple.
The symptom
I deployed. The build passed. e4z-xyz.pages.dev served the new content. dashboard.e4z.xyz served content from two weeks ago. Cache purges did nothing. Hard refreshes did nothing. A full purge_everything did nothing.
The tell
Headers don't lie:
$ curl -sI https://dashboard.e4z.xyz/ | grep -iE 'x-opennext|x-powered'
x-powered-by: Next.js
x-opennext: 1
$ curl -sI https://e4z-xyz.pages.dev/ | grep -iE 'x-opennext|x-powered'
# (nothing)
x-opennext: 1 is emitted by OpenNext's Worker runtime. Pages doesn't emit that header. Something Worker-shaped was in the request path on the custom domain but not on the project domain.
The cause
Two layers of shadowing:
- An old Worker named
e4z-dashboardstill haddashboard.e4z.xyzbound as a custom domain. - A zone-level wildcard Worker route
*.e4z.xyz/*pointed at a script callede4z-wildcard-test— a leftover experiment from three weeks prior.
The wildcard was the real killer. It swallowed every subdomain in the zone. Cloudflare's routing precedence puts Worker custom domains and routes above Pages custom domains, so DNS was essentially irrelevant.
The fix
In order:
- Remove the Worker's custom domain (
e4z-dashboard→ Settings → Triggers). - Delete the wildcard route and its script.
- Remove and re-add
dashboard.e4z.xyzas a custom domain on the Pages project. The binding gets into a weird state after being shadowed for a while; a clean re-add reprovisions the SSL cert and rebuilds the routing. - Purge cache.
Page served. Ten minutes of head-scratching, then it just worked.
What I'd do differently
- Name test Workers like test Workers.
e4z-wildcard-testis the right name, but routing scope is what matters. A wildcard route is a production foot-gun even if the script is called-test. - Prefer specific routes over wildcards.
order.e4z.xyz/*is fine.*.e4z.xyz/*is a trap. x-opennext/x-powered-byare your friends. If a Pages site's custom domain is serving something unexpected, check the headers before anything else — they'll tell you immediately whether you're actually hitting Pages.
Next one up: moving an old Next.js site from Vercel to Cloudflare Pages, and where the Prisma adapter falls over.