Redirect Chains and Loops: How to Find and Fix Them
Practice4 minInnovialabs editorial team
In short
A redirect chain is several hops before the final page, and a loop never reaches one; fix both by pointing every old URL and internal link straight to the final address with a single 301 or 308.
The short answer
A redirect chain happens when a URL redirects to another URL that redirects again before reaching the final page: A → B → C. A redirect loop happens when the hops never end: A → B → A.
The fix is the same in both cases: every old address should lead to the final URL in one hop, and internal links, canonicals and sitemaps should point to the final URL directly, with no redirect at all.
Why chains and loops hurt
- Wasted crawl budget. Every hop is a separate request. On large sites, robots spend time on redirects instead of new pages.
- Diluted signals. Each extra hop is another chance for something to break: a wrong code, a lost parameter, a temporary redirect in the middle.
- Crawl limits. Search engines follow only a limited number of hops. If the chain is too long, the page is not reached and is reported as a redirect error.
- Slower pages. Users and browsers also wait for each hop, which is especially noticeable on mobile networks.
- Loops break the page completely. Browsers show an error, and search engines cannot index the URL.
Typical sources
- Protocol and host normalization in separate steps:
http://example.com→https://example.com→https://www.example.com. - Trailing slash rules added on top of other rules.
- Several site migrations, each adding its own redirects to the old ones.
- Conflicting rules between the server, the CDN and the application. A classic loop: the app redirects to HTTPS because it sees HTTP behind a proxy, while the proxy already serves HTTPS.
How to find them
With a crawler
Run a site crawler (Screaming Frog, Sitebulb or similar) from the main page and also on a list of old URLs. Look for:
- URLs with more than one redirect hop;
- internal links that point to redirecting URLs;
- redirects that end in 4xx or 5xx;
- loops reported as errors.
With Search Console
In the Pages (page indexing) report, open the Page with redirect status. These URLs are not indexed because they redirect. This is normal for old addresses, but the list shows which redirecting URLs Google still knows about. If you see them in your sitemap or internal links, update those links. The separate Redirect error status points to chains that are too long, loops and broken redirect targets.
Manually
To see every hop for a specific URL:
curl -sIL http://example.com/old-page | grep -iE "^(HTTP|location)"
Each HTTP line is one response. More than one redirect before the final 200 means a chain.
How to fix them
- Build a table: old URL → final URL.
- Rewrite every rule so it points directly to the final URL.
- Combine normalization (protocol, host, slash) into one rule.
- Update internal links, canonical tags, hreflang and the sitemap to the final URLs.
- Recheck with the crawler.
Nginx
Redirect HTTP and the non-www host straight to the final address:
server {
listen 80;
server_name example.com www.example.com;
return 301 https://www.example.com$request_uri;
}
server {
listen 443 ssl;
server_name example.com;
# ssl_certificate and ssl_certificate_key here
return 301 https://www.example.com$request_uri;
}
Apache (.htaccess)
One rule instead of two separate steps for HTTPS and www:
RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ https://www.example.com%{REQUEST_URI} [L,R=301]
Next.js
In next.config.js, point old addresses straight to the final one. If /old once moved to /new and later to /newest, both rules should lead to /newest:
module.exports = {
async redirects() {
return [
{ source: "/old", destination: "/newest", permanent: true },
{ source: "/new", destination: "/newest", permanent: true },
];
},
};
With permanent: true Next.js returns 308, which search engines treat as a permanent redirect. Also check that the trailingSlash setting matches the URLs in your redirect destinations, otherwise Next.js will add an extra hop.