How to Fix a Redirect Loop (ERR_TOO_MANY_REDIRECTS)
Browsers stop following after 20 hops. Here's how to find the culprit on WordPress, Cloudflare, and Apache — fast.
ERR_TOO_MANY_REDIRECTS means your browser gave up. After 20 redirect hops, Chrome and Firefox bail out to prevent infinite loops. The page returns nothing — just the error.
For SEO, a redirect loop means Googlebot can't crawl the URL at all. The page effectively doesn't exist in the index.
Here's how to diagnose the loop source and fix it for the five most common configurations.
Redirect Loop vs. Redirect Chain
These are related but different problems.
A redirect loop is circular: A→B→A, or A→B→C→A. The browser or crawler never reaches a final destination and eventually gives up.
A redirect chain is linear but long: A→B→C→D→E. No loop, but each extra hop adds latency and dilutes link equity. Chains don't cause ERR_TOO_MANY_REDIRECTS — they just slow things down.
This article covers loops. For chains, see redirect chain SEO fix.
How to Diagnose With curl
Before touching any configuration, map the actual redirect path:
curl -IL --max-redirs 10 https://example.com
The -I flag returns headers only. The -L flag follows redirects. --max-redirs 10 caps the chain so curl exits instead of running forever.
The output shows each hop:
HTTP/2 301
location: https://www.example.com/
HTTP/2 301
location: https://example.com/
HTTP/2 301
location: https://www.example.com/
...
If you see the same URLs cycling, you've identified the loop. The location values in the headers tell you exactly which rules are fighting each other.
For HTTPS loops specifically, add -v to see the full headers including Strict-Transport-Security:
curl -ILv https://example.com 2>&1 | grep -E "(HTTP|location|Location)"
The 5 Common Causes and Fixes
1. WordPress HTTPS Plugin + .htaccess Conflict
How it happens: A plugin like "Really Simple SSL" forces HTTPS by adding PHP-level redirects. Meanwhile, .htaccess also has an HTTP→HTTPS redirect rule. On some server configurations, these two rules see different versions of the request and create a loop.
Diagnosis: Check your .htaccess for redirect rules and compare with your active plugins.
# .htaccess rule that might conflict:
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
If a plugin is also doing this redirect at the PHP level, you can get a loop where each system thinks the other hasn't redirected yet.
Fix: Pick one mechanism. The cleanest approach is to handle HTTPS in .htaccess and disable the plugin's redirect feature:
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Then in Really Simple SSL (or equivalent), disable the "301 redirect to HTTPS" setting while keeping the plugin active for its other features (like fixing mixed content).
2. Cloudflare Flexible SSL With Origin Redirect
How it happens: Cloudflare's "Flexible SSL" mode encrypts traffic between visitors and Cloudflare but uses plain HTTP between Cloudflare and your origin server. If your origin server has an HTTP→HTTPS redirect, here's the loop:
- Visitor hits
https://example.com - Cloudflare connects to your origin over
http://example.com - Your origin responds: redirect to
https://example.com - Cloudflare fetches
https://example.comagain... ashttp://to origin - Origin redirects again
- Loop
Fix: Change Cloudflare's SSL mode from Flexible to Full or Full (Strict). Full mode encrypts Cloudflare→origin traffic, so your origin receives HTTPS requests and doesn't redirect them.
In the Cloudflare dashboard: SSL/TLS → Overview → Full (or Full Strict if you have a valid certificate on the origin).
3. Trailing-Slash Plugin Fighting Server Config
How it happens: A WordPress plugin enforces trailing slashes on all URLs. Your server config (or another plugin) removes trailing slashes. The result: /page/ redirects to /page, which redirects back to /page/.
Diagnosis: In your curl output, look for alternating URLs with and without trailing slashes.
Fix: Choose a canonical URL format — trailing slash or no trailing slash — and enforce it in exactly one place. If you're using a caching plugin or Nginx, define the rule there and remove any plugin handling the same thing.
For WordPress + Nginx:
# Enforce no trailing slash (remove trailing slash, except on directories)
rewrite ^/(.*)/$ /$1 permanent;
Or enforce trailing slash consistently:
rewrite ^([^.]*[^/])$ $1/ permanent;
Pick one. Never both.
4. Canonical Hostname Mismatch
How it happens: Your site's canonical hostname (www vs. non-www) is set differently in multiple places:
- WordPress
siteurlsetting:https://www.example.com - .htaccess redirect: forces non-
www - Cloudflare page rule: forces
www
These three rules create a triangle: www→non-www→www→...
Diagnosis: Check all the places that define your canonical domain:
# WordPress database (run via WP-CLI)
wp option get siteurl
wp option get home
Compare with your .htaccess RewriteCond %{HTTP_HOST} rules and any Cloudflare page rules.
Fix: Make all three agree. The simplest approach:
- Set WordPress
siteurlandhometo the canonical form - Handle the redirect in .htaccess only
- Remove any conflicting Cloudflare page rules for the same hostname
5. CDN Cache Serving a Cached Redirect
How it happens: A redirect was in place at a URL, the CDN cached the 301 response, then the redirect was removed — but the CDN is still serving the cached version. The destination URL now redirects back to the original, creating a loop.
Diagnosis: Clear cache for the affected URL and test again. If the loop disappears after cache clear, this is the cause.
Fix:
- Purge the CDN cache for the affected URL(s)
- Check that your CDN isn't configured to cache 3xx responses (it generally shouldn't be)
- If using Cloudflare, use Cache → Purge Cache → Custom Purge to target specific URLs
Fixing the Common WordPress Scenario End-to-End
If you're on WordPress and not sure where to start, here's the fastest path:
Step 1. Clear your browser cache and test in an incognito window. Confirm the loop is real and not cached.
Step 2. Run the curl command to see the actual loop:
curl -IL --max-redirs 5 https://yoursite.com
Step 3. Define WP_HOME and WP_SITEURL directly in wp-config.php to lock the canonical URL:
define('WP_HOME', 'https://www.example.com');
define('WP_SITEURL', 'https://www.example.com');
This overrides whatever is in the database and prevents plugins from writing conflicting values.
Step 4. Check .htaccess for redirect rules. A clean HTTPS + www enforce looks like this:
RewriteEngine On
# Force HTTPS
RewriteCond %{HTTPS} off
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Force www
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
Step 5. Deactivate all redirect/SSL plugins temporarily and test. If the loop resolves, reactivate plugins one at a time to find the conflict.
Validating the Fix
After fixing:
curl -IL https://example.com
You should see a clean chain ending in a 200 with no cycles. For the canonical URL checker, paste in your URLs to confirm the final destination is consistent with your intended canonical:
How Redirect Loops Affect SEO
A looping URL is completely uncrawlable — Googlebot will abandon it just like a browser does. If the loop is on your homepage, it's catastrophic. On an internal page, it removes that URL from the index entirely.
After fixing the loop, submit the affected URLs through Search Console's URL Inspection tool to trigger a fresh crawl. Check the coverage report over the following days to confirm the URLs move from "Error" to "Indexed."
Redirect loops and redirect chains are both part of technical SEO plumbing that needs to be clean before content quality matters. See redirect chain SEO fix for the related (but different) problem of long redirect chains that aren't loops.