Vercel vs Cloudflare Pages Redirect Loops
You move a site from Vercel to Cloudflare Pages (or put Cloudflare in front of Vercel). Browser shows ERR_TOO_MANY_REDIRECTS. curl shows a 301 ping-pong between https://example.com and https://www.example.com, or between HTTP and HTTPS. Each platform’s “force HTTPS” / “www redirect” is correct in isolation. Together they form a loop. Clearing cookies does not fix a policy collision.
Classic loop topologies
Apex ↔ www disagreement. Cloudflare Redirect Rule sends apex → www. Vercel domain settings send www → apex. Endless 301.
HTTP ↔ HTTPS double enforcement. Origin redirects HTTP→HTTPS with an absolute Location to host A. Edge also redirects but rewrites host to B. Scheme or host flip-flops.
Trailing slash wars. Framework redirects /foo → /foo/. CDN rule redirects /foo/ → /foo.
Duplicate config. Old vercel.json redirects still deployed while Cloudflare Bulk Redirects encode the opposite target. Preview host rules accidentally applied globally.
Debug with curl, not the browser
Browsers cache redirects aggressively. Map hops explicitly:
curl -sI https://example.com | egrep -i 'HTTP/|location|cf-|server|x-vercel'
curl -sI https://www.example.com | egrep -i 'HTTP/|location|cf-|server|x-vercel'
curl -sI http://example.com | egrep -i 'HTTP/|location'
List each Location on paper. Note which hop adds cf-ray versus x-vercel-id — that tells you who issued the redirect. A healthy cutover shows zero or one hop to the canonical URL.
One canonical host, one owner
Pick exactly one canonical: https://example.com or https://www.example.com. Then:
- DNS resolves both names appropriately (proxied or not — know which).
- A single 301 from the non-canonical host to the canonical.
- HTTPS termination consistent end-to-end.
| Setup | Recommendation |
|---|---|
| Cloudflare proxy → Vercel origin | Host redirects at Cloudflare; disable opposing Vercel www/apex redirects |
| Cloudflare Pages only | One of: _redirects, Bulk Redirects, or Wrangler — not three copies |
| Vercel only | Domain panel + vercel.json — no opposite registrar/CDN rules |
Flexible SSL is a famous footgun
Cloudflare SSL mode Flexible talks HTTP to the origin. If the origin forces HTTPS redirects to the public hostname, edge and origin bounce forever. Use Full (strict) with a valid origin certificate (or Cloudflare Origin CA) when the orange cloud is on. Flexible + “Always Use HTTPS” + origin HTTPS redirect is the incident report you already wrote last year.
Inventory every redirect source
During migration, list and disable until one remains:
- Cloudflare Redirect Rules, legacy Page Rules, Bulk Redirects, Transform Rules, Workers returning
Response.redirect - Vercel project Domains,
vercel.jsonredirects/rewrites, Next middleware redirects - Framework trailing-slash / basePath settings
- DNS-only vs proxied — DNS-only bypasses Cloudflare redirect features
Delete or disable the old platform’s host redirects before enabling the new ones when possible. Path-level marketing redirects can wait until the host is stable.
Incident fix sequence
- If proxied, set SSL to Full (strict).
- Disable www/apex redirects on one side entirely.
- Confirm curl hop list length is 0–1.
- Re-enable a single documented host redirect.
- Purge CDN cache; verify in a private window.
- Only then restore path-level redirects.
Trailing slash and case
trailingSlash: true in Next/Astro must match CDN rewrites. Auth gateways that normalize case can also bounce. Treat slash policy as part of the same “one owner” rule.
Redirect loops are almost never “DNS propagation” and almost never “clear cookies.” They are two owners of Location headers disagreeing. Make one owner, verify with curl -I, keep the other platform passive on host canonicalization.