ERR_TOO_MANY_REDIRECTS in Chrome. "The page isn't redirecting properly" in Firefox. Both mean the same thing: the browser followed a chain of redirects, noticed it had arrived somewhere it had already been, and gave up.
A loop is always two rules disagreeing. Each is doing exactly what it was told; the problem is that together they contradict each other. Finding it is a matter of identifying which pair.
First, see the actual chain
Guessing is slow. Run the URL through the Redirect Checker — it follows each hop manually and shows every URL and status code, then flags the exact hop that returns somewhere already visited.
That output usually identifies the cause on its own. A chain alternating between example.com and www.example.com is the www conflict. A chain that keeps returning to the same https:// URL is the proxy problem. From a terminal:
curl -sSIL -o /dev/null -w '%{http_code} %{url_effective}\n' https://example.com/
Cause 1: www and non-www fighting
The most common by a distance.
One rule says "always add www". Another says "always remove www". Each fires on the other's output, forever:
example.com -> www.example.com -> example.com -> www.example.com -> …
This normally happens because the rule exists in two places. A CMS is configured with one canonical hostname while the web server or CDN enforces the other. WordPress in particular stores its own Site Address and will redirect to it, so a server rule pointing the other way loops immediately.
The fix: pick one hostname and make sure only one place enforces it. Check the server config, the CMS site URL setting, any CDN page rules, and .htaccess — the duplicate is usually in the one you did not write.
Cause 2: HTTPS redirect behind a proxy
The second most common, and the most confusing, because every rule involved looks correct.
Your site sits behind a CDN, load balancer or reverse proxy. That proxy terminates TLS and forwards the request to your origin over plain HTTP — which is normal and intended. The origin sees an insecure connection, applies its "always redirect to HTTPS" rule, and sends the visitor to the HTTPS URL. The proxy receives that request, terminates TLS again, forwards over HTTP again, and the loop closes.
The tell is that the chain repeats the same https:// URL without ever changing hostname.
The fix: the origin must stop inspecting the connection it can see and trust the X-Forwarded-Proto header the proxy sets.
In Apache:
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
In Nginx behind a proxy:
if ($http_x_forwarded_proto != "https") {
return 301 https://$host$request_uri;
}
For WordPress specifically, this is what wp-config.php needs before the redirect rule can behave:
if (isset($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] === 'https') {
$_SERVER['HTTPS'] = 'on';
}
If you are on Cloudflare, the same loop appears when SSL mode is set to Flexible. Flexible means Cloudflare talks HTTPS to the visitor and plain HTTP to your origin — which guarantees this loop on any origin that redirects to HTTPS. Set it to Full (strict) instead.
Cause 3: trailing slash rules
One rule adds a trailing slash, another removes it:
/about -> /about/ -> /about -> …
Usually a CMS or framework enforcing one convention while the web server enforces the other. Decide which form is canonical and remove the competing rule.
Cause 4: a CMS redirect plugin looping on itself
A redirect entry whose source and destination overlap. The classic version is a rule redirecting /blog to /blog/post while a wildcard rule sends /blog/* back to /blog.
Wildcard and regex rules are the usual culprits, because their scope is wider than the person writing them expected. Disable the redirect plugin briefly: if the loop stops, the conflict is in its rule list.
Cause 5: login or authentication loops
You sign in, get sent to a protected page, which decides you are not signed in and sends you back to the login page.
This is usually a session or cookie problem rather than a redirect problem. Common causes are a cookie domain mismatch — set for www.example.com while you are browsing example.com — a session store that is not shared across multiple application servers, or a Secure cookie on a page still being served over HTTP.
Working through it
- Trace the chain with the Redirect Checker and note whether the hostname changes between hops. Changing hostname points at cause 1. Same URL repeating points at cause 2.
- Test in a private window. Browsers cache 301s aggressively, so you may be seeing an old rule that no longer exists on the server.
- List every place that can redirect. Server config,
.htaccess, CMS settings, redirect plugins, CDN page rules. The duplicate rule is almost always somewhere you had forgotten could redirect. - Disable rules one at a time, re-testing between each. Two changes at once tells you nothing about which one mattered.
- Check the proxy headers if a CDN or load balancer is involved.
Why it seems intermittent
Two things make a loop look like it comes and goes, which sends people chasing ghosts.
Browser caching. A cached 301 keeps redirecting from the browser's own memory without contacting your server. The loop persists for you after the fix and is gone for someone testing on a fresh machine.
Partial rules. A rule scoped to certain paths, hostnames or logged-in states loops only for requests matching it. /about loops, the homepage is fine, and it looks random until you notice the pattern.
Both are why a private window and a second device are worth more than another round of config edits.
After fixing it
Re-trace the URL and confirm the chain terminates at a 200. While you are there, check the length — a loop is often introduced while adding a rule to an already-stacked chain, and collapsing several hops into one is worth doing while you have the config open. See 301 vs 302 for choosing the right code for the rule you end up with.