/ Redirect Checker Fixes / CDN-Origin Conflicts

How to Fix CDN-Origin Redirect Conflicts

A request hits the CDN, which applies a redirect, forwards to origin, which applies its own redirect. Two hops for what should be one. Or worse — they disagree on the target, producing a loop. Almost always the result of independent teams configuring the same redirect at different layers without coordination. The fix is picking one canonical authority per redirect concern, disabling the duplicates everywhere else, and documenting the ownership so it doesn't drift again.

1. Map every redirect source

List every system that can emit a 301/302 in your stack:

LayerWhere redirects live
CDN edgeCloudflare Page Rules, Redirect Rules, Workers; Fastly VCL; AWS CloudFront Functions
Load balancerAWS ALB rules, GCP LB rules, HAProxy rules
Web servernginx config, Apache .htaccess, IIS web.config
ApplicationExpress middleware, Django URL conf, Rails routes, framework redirect helpers
CMSWordPress core canonical, SEO plugin redirects, dedicated redirect plugins
DNSDomain registrar URL forwarding, "naked domain" redirects

2. Trace a problematic URL

Step 1
curl with verbose layer identification
curl -IL -v http://example.com/affected-page 2>&1 | \
  grep -E "< HTTP|< Location|< Server|< CF-Ray|< X-"

# Sample output reveals each hop's source:
# < HTTP/1.1 301 Moved Permanently
# < Server: cloudflare           ← hop 1 from Cloudflare
# < Location: https://example.com/affected-page
# < HTTP/2 301
# < Server: nginx                ← hop 2 from origin nginx
# < Location: https://www.example.com/affected-page
# < HTTP/2 200
# < Server: nginx                ← final 200 from origin
Step 2
Identify the layer per hop
Cloudflare → Server: cloudflare, CF-Ray: ...
Fastly → X-Served-By: cache-..., Server: ...
AWS CloudFront → X-Cache: ...cloudfront, Via: ... CloudFront
nginx origin → Server: nginx
Apache origin → Server: Apache
Application server → X-Powered-By: Express, Server: gunicorn

3. Pick one canonical authority per concern

Decision matrix:

ConcernRecommended layerReason
HTTPS upgradeCDNCheapest hop, doesn't reach origin
www canonicalisationCDNSame — infrastructure concern, no app logic needed
Trailing slashCDN or web serverInfrastructure concern
Old URL → new URL (permanent moves)CDN or dedicated pluginEasy management UI, version-controlled list
A/B test routingApplicationNeeds app state (user ID, cookies)
Login redirectApplicationNeeds auth state
Geo-routingCDNCDN has user geo built-in, fastest

4. Disable redirects at conflicting layers

Example: HTTPS upgrade at Cloudflare, disable at origin

# Cloudflare rule (KEEP)
# SSL/TLS → Edge Certificates → Always Use HTTPS: ON

# nginx origin (DISABLE):
# Before:
server {
  listen 80;
  return 301 https://$host$request_uri;
}

# After (comment or remove):
# server {
#   listen 80;
#   return 301 https://$host$request_uri;
# }

# Note: must still listen on port 80 if Cloudflare connects via HTTP (Flexible SSL)
# Switch Cloudflare SSL to Full/Full Strict so it connects via HTTPS instead

Example: www canonical at Cloudflare, disable at origin

# Cloudflare Bulk Redirect or Page Rule (KEEP)
# example.com/* → https://www.example.com/$1 (301)

# Origin .htaccess (DISABLE):
# Before:
RewriteCond %{HTTP_HOST} ^example\.com$
RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]

# After (remove these lines):
# (deleted)

Example: WordPress plugin redirects, disable .htaccess conflicts

# WordPress Redirection plugin (KEEP)
# Manage redirects via WP admin UI

# .htaccess (CHECK FOR CONFLICTS):
# Look for Redirect or RewriteRule lines that duplicate plugin redirects
# Comment out duplicates
# <IfModule mod_rewrite.c>
#   # Redirect 301 /old-page /new-page  (now managed by Redirection plugin)
# </IfModule>

5. The Flexible SSL trap

⚠️ Cloudflare "Flexible SSL" mode: Cloudflare↔Browser is HTTPS, Cloudflare↔Origin is HTTP. If origin redirects HTTP→HTTPS based on its local connection scheme, every Cloudflare request triggers redirect loop.
# Bad origin config in Flexible SSL setup
if ($scheme != "https") {
  return 301 https://$host$request_uri;
}
# Origin always sees HTTP from Cloudflare → always redirects → loop

# Fix: use X-Forwarded-Proto header
if ($http_x_forwarded_proto != "https") {
  return 301 https://$host$request_uri;
}
# Cloudflare sets this header to "https" when browser→Cloudflare was HTTPS

# Better fix: upgrade Cloudflare SSL to Full or Full (Strict)
# Then origin connection is also HTTPS, $scheme is correct

6. Common cleanup patterns

Cloudflare audit — what's currently configured

Cloudflare Dashboard → Rules → Page Rules / Redirect Rules / Bulk Redirects
SSL/TLS → Edge Certificates → Always Use HTTPS
Crypto → HSTS settings

List every active rule. Compare to origin config. Identify duplicates.

nginx audit — list every redirect

grep -rE "return 30[12]|rewrite.*permanent|rewrite.*redirect" \
  /etc/nginx/ /etc/nginx/sites-enabled/ /etc/nginx/conf.d/

# Each match is a potential redirect rule. Check if it's still needed.

Apache .htaccess audit

grep -rE "Redirect |RedirectMatch |RewriteRule.*\[R" \
  /var/www/ --include=".htaccess"

# Some rules date back years and are forgotten about.

7. Document redirect ownership

Once cleaned up, document so future changes don't recreate conflicts:

# redirect-ownership.md
#
# This site's redirect layers and what each handles:
#
# === Cloudflare (edge) ===
# - HTTPS upgrade: "Always Use HTTPS" page rule
# - www canonicalisation: Bulk Redirect example.com/* → www.example.com/$1
# - Legacy URL moves: Bulk Redirect list (manually maintained, CSV in repo)
#
# === Origin nginx ===
# - Trailing slash: rewrite ^(.+[^/])$ $1/ permanent
# - No HTTPS rules (handled by Cloudflare)
# - No www rules (handled by Cloudflare)
#
# === WordPress (CMS) ===
# - Editorial URL changes: Redirection plugin
# - No infrastructure redirects (handled at layer above)
#
# === Application ===
# - Login redirect: middleware handles authenticated routes
# - A/B test routing: dynamic, based on user cookie
#
# DO NOT add infrastructure redirects (HTTPS, www, slash) at origin or below.
# When adding new redirects, check this doc to identify the correct layer.

8. Verify after cleanup

Step 1
Single-hop test
curl -IL http://example.com/affected-page 2>&1 | grep -cE "^HTTP"
# Should be 2: one 301 + final 200
# More than 2 = chain still exists
Step 2
Run Redirect Checker site-wide
Zero loops, all chains 1 hop maximum. Compare to baseline before cleanup.
Step 3
Performance test
Reduced hops → faster TTFB. WebPageTest first-byte metric typically drops 100-500ms per eliminated hop.
💡 Most CDN-origin conflicts come from one team setting up Cloudflare and a different team configuring the server. When in doubt, audit Cloudflare's dashboard first — it's the most likely source of "I didn't know that rule was there" surprises. Then audit origin config. The conflict is usually obvious once you see both rule sets side by side.

↪️ Re-run the Redirect Checker

Verify chains collapsed and no loops remain.

Run Redirect Checker →
Related Guides: Redirect Checker Fixes  ·  Fix Redirect Loops  ·  Fix HTTPS Redirects  ·  Redirect Checker Guide
💬 Got a problem?