DEV Community

lucian tudor
lucian tudor

Posted on

Cloudflare Worker redirect never runs, because static assets are served first

I wrote a www → apex redirect in my Worker's fetch handler. It was dead code from the moment I deployed it, and I didn't notice for weeks.

Matches if: your Cloudflare Worker isn't running; the fetch handler never fires; a redirect in Worker code doesn't work; console.log in a Worker produces nothing on real pages.

The symptom

The redirect logic is right there in the Worker. It's deployed. You can read it in the dashboard. And www.example.com/about serves the page instead of redirecting.

Add a console.log and it never fires. The handler isn't being slow or failing — it isn't being called.

Why

With Workers static assets, requests that match a file in your assets directory are served directly at the edge. Your Worker script is not invoked at all.

That's the default behaviour, and it's the right default — it's why static asset requests are fast and don't bill you a Worker invocation. But it means your fetch handler only runs for requests that don't resolve to a file.

So my redirect ran for exactly the URLs nobody visits. Every real page on the site had a file behind it, which meant the Worker was skipped, which meant the redirect never happened. The only requests that reached my code were 404s.

If you want one sentence for it: your Worker is the fallback, not the front door.

Confirming it
curl -sI https://www.example.com/

No 301 or 302, just a 200 with your page. Then request something that definitely doesn't exist:

curl -sI https://www.example.com/definitely-not-a-real-path

If that one redirects, you've confirmed it. Your code runs only when the asset lookup misses.

The fix

Host-level redirects don't belong in Worker code. Put them in a Redirect Rule (Rules → Redirect Rules), which runs earlier in Cloudflare's traffic sequence than Workers do.

For www → apex:

If hostname equals www.example.com
Then dynamic redirect to concat("https://example.com", http.request.uri.path), status 301, preserve query string

It's free on every plan, it runs before the asset lookup, and it's one less thing in your Worker.

The other option, and why I didn't take it

The assets config has a setting that forces the Worker to run before the asset lookup on every request. It solves the problem, and it's the right answer if you genuinely need per-request logic — auth checks, header rewriting, anything that has to see traffic your assets would otherwise swallow.

For a redirect it's the wrong trade. You'd be invoking a Worker on every single request to move a small number of visitors between two hostnames, when a Redirect Rule does it earlier and for nothing.

One warning if you do reach for it: that config key has been renamed at least once, so check the current name in the Workers static assets docs rather than copying it from a blog post — including this one.

I hit this building https://edmundwarde.com/ on Workers static assets. There's a larger trap in the same family — a wildcard Worker route silently swallowing an R2 custom domain — which I've written up separately.

Top comments (0)