Home › Web Platform › WordPress Too Many Redirects: Break the Loop
Beginner 5 min · September 23, 2026

WordPress Too Many Redirects: Break the Loop

Fix siteurl and home first — WordPress redirect loops come from URL mismatches, stale .htaccess rules, or HTTPS plugins fighting..

N
Naren Founder & Principal Engineer

20+ years shipping production backend systems. Written from production experience, not tutorials.

Follow
✓ Production
production tested
September 27, 2026
last updated
2,085
articles · all by Naren
Before you start⏱ 19 min
  • ✓SSH with WP-CLI or SFTP plus phpMyAdmin access
  • ✓Know your canonical URL (scheme, www, domain)
  • ✓Access to CDN SSL settings if behind a proxy
 ● Production Incident 🔎 Debug Guide
⚡Quick Answer
  • Redirect loops mean WordPress bounces the browser between two URLs forever — check siteurl vs home
  • Search-replace the whole database with WP-CLI after any domain or HTTPS change
  • Rename .htaccess to test for rewrite loops, then rebuild clean permalinks
  • Disable HTTPS-forcing plugins one by one; two forcers always fight
  • Purge plugin, host, and CDN caches after fixing — stale 301s replay old loops
✦ Definition~90s read
What is WordPress Too Many Redirects After URL Change?

An HTTP redirect tells the browser to load a different URL instead — a 301 (permanent) or 302 (temporary) response with a Location header. Redirects are normal: http to https, non-www to www, old slugs to new ones. A loop happens when the chain never terminates — A points to B, B points back to A — and browsers abort after roughly 20 hops with the too-many-redirects error.

★
Think of a receptionist who sends you to room A, where a sign sends you back to reception, forever.

WordPress creates redirects from four layers that don't know about each other. The siteurl and home options redirect requests that don't match the canonical address. The .htaccess file (Apache) applies rewrite rules before WordPress even boots. HTTPS plugins add their own forcing redirects.

The server or CDN (Nginx rules, Cloudflare Flexible SSL) can add a fourth layer. Any two layers with opposite opinions — one forcing www, another stripping it — loop forever.

Migrations trigger most loops because the database keeps the old domain in dozens of places: options, posts, meta, widgets. If siteurl says the new domain but post content and .htaccess still reference the old one (or http instead of https), requests ping-pong between past and present. The fix is making every layer agree on one canonical URL, which the steps below do in order.

Plain-English First

Think of a receptionist who sends you to room A, where a sign sends you back to reception, forever. Your browser gives up after about 20 laps and shows the redirect error. The fix is making both signs agree: one canonical address, one set of directions, no contradictions between WordPress settings, the server file, and your HTTPS plugin. Browsers quit after about 20 laps, which is why the error names excess rather than the cause.

You visit your site and the browser spins, then surrenders: ERR_TOO_MANY_REDIRECTS. The page never loads — Chrome, Firefox, and Safari all bounce between addresses until they quit. It usually strikes right after a migration, a domain change, or turning on HTTPS, which makes it feel like the move itself broke everything.

A redirect loop is exactly what it sounds like. URL A redirects to URL B, which redirects back to URL A, and your browser laps the circuit 20 times before giving up. WordPress has several independent redirect sources — the siteurl/home settings, .htaccess rewrite rules, HTTPS-forcing plugins, server configs — and any two disagreeing creates a loop.

This guide hunts the loop methodically. You'll compare siteurl and home, search-replace stale URLs across the database, test .htaccess by renaming it, and untangle HTTPS plugin fights. Each step eliminates one suspect, so the loop has nowhere left to hide. You'll need Site access plus the hosting or CDN panel — loops often span layers owned by different dashboards.

See the Loop With curl Before Touching Anything

Browsers hide redirect chains — they just report giving up. curl shows every hop: status codes and Location headers in order. One command prints the whole story, and an alternating A-B-A-B pattern names the two disagreeing layers instantly. This output is your map; every fix below gets verified against it.

Read the chain carefully. A single 301 from http to https followed by a 200 is healthy. The same pair of URLs repeating 20 times is the loop. Note the scheme (http vs https), www vs non-www, and trailing slashes at each hop — the flip-flopping element is the disagreement. Also watch for mixed 301/302 types; cached 301s in browsers replay old loops after server fixes.

Save the output before and after each fix. Comparing chains proves progress when the browser still shows the error from its own cache. Add the -L flag contrast deliberately: without it curl prints hops, with it curl follows them — for loop diagnosis you want the printed hops. Save both outputs with timestamps; the pair documents the loop shape for tickets and post-mortems. If curl shows a clean chain but your browser still loops, the problem is now local cache — clear it or test incognito before undoing a fix that actually worked.

BASH
1
2
3
4
5
6
7
8
9
10
# Print the full redirect chain (status + destination per hop)
curl -sIL https://yoursite.com | grep -Ei '^(HTTP/|location:)'

# Healthy: one 301 to canonical, then 200
# HTTP/2 301 ... location: https://yoursite.com/
# HTTP/2 200

# Looping: the same two locations alternate ~20 times
# location: https://yoursite.com/
# location: http://yoursite.com/  <-- flips back: loop found
📊 Production Insight
Always capture the chain with curl first. Twenty minutes of .htaccess edits without chain output is guessing — two curl runs turn it into diagnosis.
🎯 Key Takeaway
curl -sIL reveals the hop chain — alternating URLs name the two layers fighting.

Align siteurl and home After Migrations

siteurl is where WordPress core lives; home is what visitors type. After migrations these often disagree — one holding the old domain, staging URL, or http scheme. WordPress redirects every request toward the canonical pair, so a mismatch against reality loops: the server sends visitors one way, the settings send them back.

Check both in wp_options (option names siteurl and home) or via WP-CLI. Both must match the real canonical address exactly — scheme, domain, www choice, and subdirectory included. https://example.com and https://www.example.com are different addresses to a redirect engine, and mixing them loops www-stripping against www-adding rules.

Fix with WP-CLI or the database, then verify with curl — not just the browser, which may replay a cached 301. On multisite, also check each subsite's options tables plus the network-wide domain mapping, since one stale subsite loops while the main site looks clean. Flush any domain-mapping caches after the fix; stale mappings re-break subsites hours after the main site recovers. If wp-admin is unreachable, updating the two wp_options rows directly restores access in seconds. This single check resolves the majority of post-migration loops.

BASH
1
2
3
4
5
6
7
8
9
10
# Read the current pair
wp option get siteurl
wp option get home

# Set both to the exact canonical URL (scheme + www matter)
wp option update siteurl 'https://yoursite.com'
wp option update home 'https://yoursite.com'

# Verify with curl, not just the browser (cached 301s lie)
curl -sIL http://yoursite.com | grep -Ei '^(HTTP/|location:)'
📊 Production Insight
Scheme and www are part of the address — https vs http or www vs bare mismatch loops even when the domain itself is right.
🎯 Key Takeaway
siteurl and home must equal the exact canonical URL — fix via WP-CLI and verify with curl.

Search-Replace Stale URLs Across the Database

siteurl and home are two rows; old URLs hide in thousands more — post content, meta, widgets, menus. After a domain or http-to-https change, leftover old-scheme links trigger redirect plugins and canonical logic that bounce requests back. A full search-replace is the only complete fix.

Use WP-CLI's search-replace with --all-tables across every table, always with --dry-run first to review counts. It handles serialized data correctly — string lengths inside serialized arrays get rewritten, which raw SQL UPDATEs corrupt, silently breaking widgets and theme settings. Never run a blind SQL REPLACE on postmeta or options.

Cover the scheme variants: replace http://old.com with https://new.com and old-domain with new-domain as separate passes if both changed. Include GUIDs only if you understand the feed-reader trade-off — most migrations leave guid values alone and the loop still resolves. Re-run the dry-run after replacing; identical counts confirm nothing drifted between preview and execution — a mismatch means content changed mid-migration and deserves investigation. After replacing, flush caches and re-run the curl chain. Leftover mixed-content warnings in the console point at any stragglers the replace missed, usually in widget text or custom CSS.

BASH
1
2
3
4
5
6
7
8
# Preview first — counts show what will change
wp search-replace 'http://oldsite.com' 'https://yoursite.com' --all-tables --dry-run

# Then run it for real (handles serialized data safely)
wp search-replace 'http://oldsite.com' 'https://yoursite.com' --all-tables
wp search-replace 'http://yoursite.com' 'https://yoursite.com' --all-tables

wp cache flush
📊 Production Insight
Raw SQL replaces corrupt serialized widgets because string lengths don't update. WP-CLI search-replace rewrites lengths correctly — never skip it.
🎯 Key Takeaway
Search-replace every table with WP-CLI (dry-run first) — raw SQL breaks serialized data.

Clean Conflicting .htaccess Rewrite Rules

On Apache hosts, .htaccess redirects run before WordPress boots — and stale rules from migrations, security plugins, or hand edits can fight the current settings. A rule forcing non-www while WordPress forces www loops every request without WordPress code ever running. The rename test proves it in seconds: rename .htaccess, reload, and a cleared site convicts the file.

Rebuild from the stock WordPress block, which handles permalinks and nothing else. Re-save Settings > Permalinks to regenerate it, then re-add custom rules one at a time with a curl check after each. The one that reintroduces the loop is the offender — often a http-to-https rule left behind after the plugin took over, or www rules from the old domain.

Nginx sites have no .htaccess — check the server block's return/rewrite directives and any www/https handling there instead. Redirect plugins (Redirection, Rank Math redirections) deserve the same one-at-a-time test — disable, curl, re-enable. Export each plugin's redirect list before disabling; re-importing beats rebuilding 200 rules by hand, and the export doubles as an audit trail. Log which rule was guilty; the pattern repeats on the next migration. And keep exactly one redirect owner per concern: one www rule, one https rule.

BASH
1
2
3
4
5
6
7
# Convict or clear .htaccess in 30 seconds
mv .htaccess .htaccess-backup
# reload the site — loop gone? the file did it

# Regenerate clean permalinks rules, then verify
wp rewrite flush
curl -sIL http://yoursite.com | grep -Ei '^(HTTP/|location:)'
⚠ Back Up .htaccess Before Editing
Download the current file first. A syntax error in .htaccess whites out the whole site with a 500 — worse than the loop you started with.
📊 Production Insight
Re-add custom rules one at a time with a curl check after each. The rule that brings the loop back is the one to rewrite or drop.
🎯 Key Takeaway
Rename .htaccess to test, rebuild clean permalinks, and keep one redirect owner per concern.

Settle HTTPS Plugin and CDN Fights

https forcing is the most common loop fuel. Security plugins, Really Simple SSL-style forcers, server rules, and CDN settings each add redirects — and any two with different views of the connection bounce forever. The classic is Cloudflare Flexible SSL (CDN-to-origin over http) plus a WordPress forcer: WordPress sees http, redirects to https, the CDN downgrades again, repeat.

Enforce a one-enforcer rule. Pick exactly one layer to own https redirects: preferably the server or CDN, with WordPress settings matching. If WordPress forces https, Cloudflare must be Full or stricter so origin traffic arrives as https. Deactivate forcing plugins one at a time (rename folders if admin is down) and curl after each until the chain collapses to a single 301.

Also check Settings > General shows https in both URLs once forcing is settled, and scan for mixed-content warnings — leftover http assets don't loop, but they break the padlock your launch promised. Watch for HSTS preload commitments too: once the domain preloads, any http downgrade anywhere in the chain loops browsers that refuse plain http. Test the chain from a mobile network too; carrier proxies sometimes cache redirects differently than office wifi. Document which layer owns https so the next person doesn't add a second forcer.

📊 Production Insight
Flexible SSL plus a WordPress https forcer loops by design. Full SSL is the fix — the certificate was never the problem.
🎯 Key Takeaway
One https enforcer only — match the CDN SSL mode to what WordPress expects.

Purge Every Cache After the Fix

Redirects cache aggressively. Browsers cache 301s, CDNs cache redirect responses, and caching plugins store the bounced page — so visitors keep looping after the server is fixed. Every loop fix ends with purging all three layers, or you'll undo a working fix chasing a ghost.

Purge in order: caching plugin first, then host cache, then CDN cache. Then verify with curl (which bypasses browser cache) before declaring victory. Test the real browser in incognito or after clearing site data — a normal reload replays the cached 301 and lies to you.

Tell affected users to hard-refresh if reports linger; there's nothing left to fix server-side once curl shows a clean chain. Staff browsers and office DNS caches are the usual stragglers — confirm with an external uptime checker's clean result before announcing the all-clear. Keep a known-looping test URL in your monitor suite; synthetic redirect checks catch regressions within minutes of the next deploy. Store the purge sequence next to the deploy button, not buried in a wiki nobody opens during launches. Test the monitor URL after each deploy; silent monitors help nobody. Add the purge sequence to your migration runbook next to the search-replace step, because the next migration will need both again.

📊 Production Insight
curl bypasses browser cache — trust it over your tab. A clean curl chain plus a looping browser means cached 301s, not a broken server.
🎯 Key Takeaway
Purge plugin, host, and CDN caches after every loop fix — then verify in incognito.
● Production incidentPOST-MORTEMseverity: high

HTTPS Launch Looped Checkout for 96 Minutes

Symptom
Minutes after the 10:00 HTTPS launch announcement, every page — homepage, cart, checkout — showed ERR_TOO_MANY_REDIRECTS. The loop ran http to https and back, about 20 hops per visit. Checkout was fully dead for 96 minutes with roughly 480 failed sessions, and the launch email kept driving fresh visitors into the loop.
Assumption
The team blamed the new SSL certificate and spent 40 minutes reissuing and reinstalling it. Then they blamed .htaccess and edited rewrite rules three times. Nobody checked the Cloudflare SSL setting because the certificate itself tested valid in every checker tool.
Root cause
Cloudflare was set to Flexible SSL (Cloudflare-to-origin over http) while the Really Simple SSL plugin forced https at WordPress level. Each visit went: browser to Cloudflare over https, Cloudflare to origin over http, WordPress saw http and redirected to https, repeat — 20 hops, then the browser quit. The certificate was fine; the two HTTPS layers held opposite views of the connection.
Fix
They switched Cloudflare from Flexible to Full SSL so origin traffic stayed https, cleared the plugin cache and Cloudflare cache, and verified the redirect chain with curl showing a single 301. They also pinned the SSL mode in the launch checklist and added a post-launch curl check that fails the deploy on any chain longer than 2 hops.
Key lesson
  • Flexible SSL plus any WordPress HTTPS forcer is a guaranteed loop. Use Full SSL (or stricter) whenever WordPress itself redirects to https.
  • Check redirect chains with curl, not just certificates. The cert was valid the whole 96 minutes — only the 20-hop chain revealed the real fault.
  • Launch checklists need a redirect-chain assertion. One curl command after go-live would have caught the loop in 10 seconds instead of 96 minutes.
Production debug guideFive steps — see the chain, fix URLs, clean rewrites, settle HTTPS, clear caches.5 entries
Symptom · 01
Browser shows ERR_TOO_MANY_REDIRECTS on every page
→
Fix
Run curl -sIL yoursite.com | grep -E '^(HTTP|location)' to print the actual hop chain. If you see the same two URLs alternating (http then https then http), note which layer each hop comes from — that's your loop, and every step below targets one side of it.
Symptom · 02
Loop started after a migration, domain, or HTTPS change
→
Fix
Check wp_options rows siteurl and home — both must show the exact canonical URL including https and www choice. Then run wp search-replace 'old-url' 'new-url' --all-tables --dry-run, review the counts, and rerun without --dry-run. Skipping serialized data handling corrupts widgets, so always use WP-CLI or a serializer-aware tool, never raw SQL.
Symptom · 03
Loop persists with correct siteurl and home
→
Fix
Rename .htaccess to .htaccess-off via SFTP and reload. If the loop stops, the file holds conflicting rules — restore the default WordPress block, re-save permalinks in Settings, and re-add custom rules one at a time. Keep a backup of the original file before editing.
Symptom · 04
Loop bounces specifically between http and https
→
Fix
Deactivate HTTPS-forcing plugins one by one (rename their folders if admin is unreachable) and check the CDN SSL mode — Cloudflare must be Full, not Flexible, when WordPress forces https. Keep exactly one HTTPS enforcer across plugin, server, and CDN layers.
Symptom · 05
Loop fixed for you but visitors still report it
→
Fix
Purge every cache layer: the caching plugin, the host cache, and the CDN cache. Then retest in an incognito window — browsers cache 301s aggressively, and a cached redirect replays the old loop long after the server is fixed.
Redirect Loop Causes Compared
Root CauseHow to ConfirmFixPrevention
siteurl/home mismatchOptions show old domain, staging URL, or httpUpdate both to the exact canonical URLVerify the pair in every migration checklist
Stale URLs in contentOld domain in posts/meta after options look rightWP-CLI search-replace across all tablesAlways search-replace after domain or scheme changes
Conflicting .htaccess rulesLoop stops when .htaccess is renamed awayRebuild clean permalinks; re-add rules singlyKeep one redirect owner per concern; back up first
HTTPS plugin vs CDN fightChain alternates http and https ~20 timesOne enforcer only; CDN on Full SSLDocument the https owner; assert chains post-launch
⚙ Quick Reference
4 commands from this guide
FileCommand / CodePurpose
curl -sIL https://yoursite.com | grep -Ei '^(HTTP/|location:)'See the Loop With curl Before Touching Anything
wp option get siteurlAlign siteurl and home After Migrations
wp search-replace 'http://oldsite.com' 'https://yoursite.com' --all-tables --dry...Search-Replace Stale URLs Across the Database
mv .htaccess .htaccess-backupClean Conflicting .htaccess Rewrite Rules

Key takeaways

1
Print the hop chain with curl before changing anything.
2
siteurl and home must equal the exact canonical URL.
3
Search-replace all tables with WP-CLI
never raw SQL.
4
Rename .htaccess to convict or clear rewrite rules fast.
5
Keep exactly one HTTPS enforcer; use Full SSL with CDNs.
6
Purge all caches and verify incognito after the fix.

Common mistakes to avoid

5 patterns
×

Editing rules without reading the curl chain

Symptom
Three rounds of .htaccess edits change nothing because the loop lives in the CDN layer, and each edit risks a 500 from a syntax slip.
Fix
Run curl -sIL first and after every change. The alternating URLs name the guilty layers — edit only those.
×

Running raw SQL replaces on the database

Symptom
Widgets and theme settings silently break because serialized string lengths no longer match, adding a second mystery to the loop.
Fix
Use WP-CLI search-replace or a serializer-aware migrator for every URL change, with --dry-run first.
×

Forgetting www vs non-www is part of the URL

Symptom
Domain and scheme match but the loop persists — one layer adds www while another strips it, forever.
Fix
Pick one canonical form (https + www or https bare) and enforce it in exactly one layer with settings matching.
×

Stacking two HTTPS forcers

Symptom
Plugin plus CDN plus server rules each redirect, and Flexible-style downgrades turn the chain into a 20-hop loop.
Fix
Keep one https enforcer. With WordPress forcing https, set Cloudflare to Full SSL or stricter.
×

Trusting a cached browser after the fix

Symptom
The server chain is clean but your tab still loops on a cached 301, so you revert a fix that actually worked.
Fix
Verify with curl and incognito. Purge plugin, host, and CDN caches before judging any loop fix.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01JUNIOR
What causes ERR_TOO_MANY_REDIRECTS on WordPress?
Q02JUNIOR
How do siteurl and home cause loops after migration?
Q03SENIOR
Why must URL migration use WP-CLI search-replace instead of raw SQL?
Q04SENIOR
How does Cloudflare Flexible SSL loop with WordPress HTTPS plugins?
Q05SENIOR
curl shows a clean chain but the browser still loops. What now?
Q01 of 05JUNIOR

What causes ERR_TOO_MANY_REDIRECTS on WordPress?

ANSWER
A redirect loop — two or more layers (siteurl/home settings, .htaccess, HTTPS plugins, CDN) sending the browser in a circle. Browsers quit after ~20 hops. I'd print the chain with curl -sIL to see which URLs alternate, then align the disagreeing layers.
FAQ · 6 QUESTIONS

Frequently Asked Questions

01
Why does the loop appear right after migration?
02
Can I fix it without wp-admin access?
03
Is it the SSL certificate's fault?
04
Does www vs non-www really matter?
05
Why did the fix work for curl but not my browser?
06
How do I stop loops on future launches?
N
Naren Founder & Principal Engineer

20+ years shipping production backend systems. Written from production experience, not tutorials.

Follow
✓ Verified
production tested
September 27, 2026
last updated
2,085
articles · all by Naren
🔥

That's WordPress. Mark it forged?

5 min read · try the examples if you haven't

←
Previous
WordPress Fatal Error: Allowed Memory Size Exhausted
4 / 5 · WordPress
Next
WordPress REST API Returns 401 for Logged-In User
→