Why your clean URLs 307-loop on Cloudflare
There is a corridor in every game that new players find at least once: the one where the door at the end drops you back at the door at the start, forever. It’s not a puzzle. Nobody hid a lever. Someone just wired the exit to the entrance. I built that corridor on a live website, twice, and both times I stood there insisting it must be something clever. It never is.
The short version: if a clean URL like /blog loops until the browser gives up (ERR_TOO_MANY_REDIRECTS) on Cloudflare Pages, delete the rewrite rule you added to _redirects. Cloudflare already serves clean URLs for you — your rule is fighting the thing that already works.
The symptom
One clean path — /blog, /careers, whatever — starts returning ERR_TOO_MANY_REDIRECTS. The page is completely dead: browsers refuse it, and crawlers and answer-engines silently drop it from the index. Everything else on the site is fine, which is what makes it maddening.
Why it happens
Cloudflare Pages already does clean URLs natively when the file exists:
- It serves
blog.htmlat/blog(a normal200). - It auto-redirects
/blog.html→/blogso you never see the ugly version.
Then someone — often me, mid-SEO-pass — adds a “helpful” line to the root _redirects file:
/blog /blog.html 200
Now you’ve told Cloudflare to send /blog to /blog.html… which it auto-redirects back to /blog… which your rule sends to /blog.html again. That’s the corridor: an infinite 307 loop between /blog and /blog.html.
The rewrite rule wasn’t fixing the clean URL. The clean URL already worked. The rule was the bug.
The fix
- Keep
_redirectsempty of those rewrites. If a matching.htmlfile exists, Cloudflare handles the clean URL for you — do nothing. - Only add a
_redirectsrule for a genuine path change with no matching file (e.g. an old URL that moved). That’s the one case rewrites are for. - Your canonical tags,
og:url, sitemap, and nav links should already point at the clean URLs — so removing the rule is also SEO-positive, not just a fix.
Verify it in one line
After deploy, don’t trust the browser cache — check the hops directly:
curl -sSL -o /dev/null -w "%{http_code} redirects=%{num_redirects}\n" https://yoursite.com/blog
You want 200 redirects=0. If you see a big redirect count, the corridor is still wired.
Why it keeps coming back
This bug recurs, and that’s the real lesson. Every time you do an SEO or AI-search pass, the clean canonical/sitemap URLs sit there looking like they need a rewrite rule to “point” at the real file. They don’t. The tidy-minded version of you will re-add the loop six months later. Leave a comment in _redirects saying why it’s empty — write the note that stops future-you from rebuilding the corridor.
I learned this on the same zero-backend, few-moving-parts philosophy I build everything on: most “clever” infra bugs are just two correct things told to do each other’s jobs.
I keep a log of corridors I’ve built by accident. This one’s near the top, with a note: the exit already worked — you wired it shut. That’s most self-inflicted bugs, honestly. Not a monster. Just a door pointed at the wrong door, patiently doing exactly what you told it, around the clock. I respect that. It’s basically my job too.