HTTP Redirect Chains: Why They Happen and How to Fix Them
Updated April 2026
Redirect chain on your site? Check it now.
Redirect Chain Checker →
A redirect chain is when URL A redirects to URL B which redirects to URL C before reaching the destination. Each extra hop adds latency, wastes crawl budget, and bleeds PageRank. The fix is always the same: collapse to a single direct redirect.
What causes redirect chains
- HTTP to HTTPS migration — separate rules for HTTP→HTTPS and www→non-www create two hops
- Domain migrations — old domain redirects to new domain, which then redirects to the canonical path
- CMS slug changes — WordPress keeps old redirect rules when you rename a page, adding a new hop each time
- CDN + origin both redirecting — Cloudflare redirects HTTP to HTTPS, then origin also redirects to the www version
- Trailing slash inconsistency —
/page→/page/stacks on top of the HTTP→HTTPS hop
Detect your redirect chain
# Follow all redirects and show each hop curl -sI -L https://example.com | grep -E "HTTP/|Location:" # Example — 3-hop chain: # HTTP/1.1 301 Moved Permanently # Location: https://example.com/ # HTTP/1.1 301 Moved Permanently # Location: https://www.example.com/ # HTTP/1.1 200 OK
Fix redirect chains — by stack
Nginx
# Wrong — two server blocks creating a chain
server {
listen 80;
server_name example.com www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl;
server_name www.example.com;
return 301 https://example.com$request_uri; # chain!
}
# Right — one block handles both HTTP and www in one hop
server {
listen 80;
listen 443 ssl;
server_name www.example.com;
return 301 https://example.com$request_uri;
}
server {
listen 443 ssl;
server_name example.com;
# canonical — no redirect
}
Cloudflare
# Wrong: separate rule for HTTP→HTTPS + separate rule for www→non-www = two hops
# Right: one Redirect Rule handles both
# Rules → Redirect Rules → Create Rule
# When: (http.host eq "www.example.com") or (http.scheme eq "http")
# Then: https://example.com${http.request.uri.path} — 301
# Gotcha: "Always Use HTTPS" + a Page Rule for HTTPS = chain
# Pick one. Disable the Page Rule and use "Always Use HTTPS" alone.
Vercel
// vercel.json
{
"redirects": [
{
"source": "/old-page",
"destination": "/new-page",
"permanent": true
}
]
}
// If /old-page → /intermediate and /intermediate → /new-page both exist:
// Add /old-page → /new-page directly and remove /old-page → /intermediate
Apache
# Wrong — multiple RewriteRules creating hops
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\. [NC]
RewriteRule ^ https://example.com%{REQUEST_URI} [R=301,L]
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
# Right — one rule handles both www and HTTP
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\.example\.com$ [OR]
RewriteCond %{HTTPS} off
RewriteRule ^ https://example.com%{REQUEST_URI} [R=301,L]
Next.js
// next.config.js
module.exports = {
async redirects() {
return [
{
source: '/old-path',
destination: '/final-destination', // skip any intermediate
permanent: true,
},
]
},
}
The most common chain — HTTP + www
http://www.example.com → https://www.example.com (HTTP→HTTPS — hop 1) → https://example.com (www→non-www — hop 2) → 200 OK (destination) Fix: send http://www.example.com/* directly to https://example.com/* One rule. One hop.
Cloudflare: "Always Use HTTPS" adds an automatic HTTP→HTTPS redirect. If you also have a Page Rule for HTTP — you have a chain. Disable the Page Rule and let "Always Use HTTPS" handle it alone.
Redirect chains vs redirect loops
| Type | What happens | Browser error | Fix |
|---|---|---|---|
| Redirect chain | A → B → C → destination | Slow load, no error | Collapse to A → destination |
| Redirect loop | A → B → A (infinite) | ERR_TOO_MANY_REDIRECTS | Find the circular rule, remove it |
SEO and performance impact
- PageRank dilution — each hop reduces link equity passed to the final URL
- Crawl budget — each hop consumes crawl budget before Googlebot reaches the canonical URL
- Latency — each hop is a full HTTP round trip, adding 100-300ms per hop
- Core Web Vitals — redirect chains delay the first byte, pushing LCP scores down
Verify the fix
# Should now show one hop maximum curl -sI -L http://www.example.com | grep -E "HTTP/|Location:" # HTTP/1.1 301 Moved Permanently # Location: https://example.com/ # HTTP/1.1 200 OKCheck your redirect chain → Redirect Chain Checker