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..
20+ years shipping production backend systems. Written from production experience, not tutorials.
- ✓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
- 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
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.
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.
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.
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.
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.
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.
HTTPS Launch Looped Checkout for 96 Minutes
- 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.
| File | Command / Code | Purpose |
|---|---|---|
| curl -sIL https://yoursite.com | grep -Ei '^(HTTP/|location:)' | See the Loop With curl Before Touching Anything | |
| wp option get siteurl | Align 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-backup | Clean Conflicting .htaccess Rewrite Rules |
Key takeaways
Common mistakes to avoid
5 patternsEditing rules without reading the curl chain
Running raw SQL replaces on the database
Forgetting www vs non-www is part of the URL
Stacking two HTTPS forcers
Trusting a cached browser after the fix
Interview Questions on This Topic
What causes ERR_TOO_MANY_REDIRECTS on WordPress?
Frequently Asked Questions
20+ years shipping production backend systems. Written from production experience, not tutorials.
That's WordPress. Mark it forged?
5 min read · try the examples if you haven't