ERR_TOO_MANY_REDIRECTS is the error Chrome shows when a page keeps sending the browser somewhere else and never arrives. Firefox calls it “The page isn’t redirecting properly”, and Safari says too many redirects occurred. If you are a visitor, four quick checks below rule out your own browser; after that, the fault is the site’s. If you own the site, the cause is nearly always two rules that disagree. Trace the hops with the free Redirect Checker or curl first, read what flips between them, and that tells you which two rules to look at.
Chrome’s limit is a constant in its network code[1], taken from the Fetch Standard[3]. Firefox sets it as a preference[4]. Google[27]; curl[9]. Apple does not publish a number for Safari.
What ERR_TOO_MANY_REDIRECTS means
A redirect is a server’s way of saying “not here, go there”. It answers with a 3xx status code and a Location header, and the browser requests the new address without asking you. One redirect is normal. A redirect loop is a chain that comes back to an address it has already visited, so it can never end.
The HTTP standard asks every client to watch for this: “A client SHOULD detect and intervene in cyclical redirections”[7]. Browsers do it with a counter. The Fetch Standard says that when a request’s redirect count reaches 20, the browser returns a network error[3]. Chrome’s network code sets the same limit, 20, and fails the next redirect with ERR_TOO_MANY_REDIRECTS[1]. Firefox’s default is also 20[4].
So the error does not mean the site has 21 redirects on purpose. In nearly every case the same two or three URLs repeated until the counter ran out. Each browser words it differently:
| What you see | Redirect limit | |
|---|---|---|
| Chrome | "[site] redirected you too many times." with ERR_TOO_MANY_REDIRECTS and "Try deleting your cookies." | 20 |
| Firefox | "The page isn't redirecting properly" | 20 (network.http.redirection-limit) |
| Safari | Safari can't open the page because too many redirects occurred | Not published by Apple |
Sources: Chrome[2]; Firefox[5][4]; Safari[6].
If you are a visitor: four quick checks
Start with the caveat. A redirect loop is almost always the site’s fault, and nothing on your side fixes a broken server. But a stored cookie can cause a loop or keep one going, and that is quick to rule out. Chrome’s own error page suggests deleting cookies[2], and Apple’s support article names outdated data in the browser’s cache or cookies as one possible cause[6].
- Open the page in a private window. If it loads there, something stored in your normal window is involved, most likely a cookie.
- Delete the cookies and cached data for that one site. You do not need to clear everything. In Safari on a Mac, Apple’s steps are: Privacy settings, Manage Website Data, search for the site, then Remove[6]. Expect to be signed out of that site.
- Try another browser, or another network. Switch from Wi-Fi to mobile data, or turn off a VPN. If the page works elsewhere, the problem is local. If it fails everywhere, it is the site.
- Check your device’s date and time. This one is rare. Browsers judge whether a cookie has expired by their own clock: a cookie is expired “if the cookie has an expiry date in the past”[8]. If your clock is far ahead, a sign-in cookie can be thrown away as soon as it arrives, and some sites will keep redirecting you to set it again.
If all four fail, the site is broken, at least for you. Tell the owner, and send them the rest of this page.
If you own the site: find the loop before you fix it
Most advice jumps straight to fixes. Hold off. Changing settings at random can swap one loop for another, and a browser can remember a permanent redirect: Next.js’s docs note that a 308 tells clients to cache the redirect forever[17]. So a real fix can look as if it failed. Trace the loop first, in a tool that starts clean each time.
The quickest way to do step 2 is the Redirect Checker. It requests each hop by hand, with automatic redirects switched off, and records the status code and Location header. It stops as soon as a URL repeats, so a loop shows up as the exact pair of URLs that point at each other. It stops at 10 redirects in any case, where Google’s crawlers stop[27].
From a terminal, curl does the same job:
# Every hop's status and Location, stopping after 10 redirects curl -sIL --max-redirs 10 https://example.com/page | grep -iE "^HTTP|^location" # The same with GET requests, for servers that answer HEAD differently curl -sL --max-redirs 10 -o /dev/null -D - https://example.com/page | grep -iE "^HTTP|^location"
-L follows redirects, and with -I it shows the headers of every response along the way. -I sends HEAD requests, so the second line repeats the trace with GET. Without --max-redirs, curl follows up to 50 redirects before it stops[9]. A loop shows as the same few location lines repeating, then an error. On Windows, run curl.exe and replace /dev/null with NUL.
Read the loop from its hops
Once you have the chain, look at what changes between one hop and the next. The part that flips back and forth points to the two rules involved.
| Likely cause | Where to look | |
|---|---|---|
| https://site/ → https://site/ (the same URL) | The server thinks the visitor came over http, because a CDN or proxy talks to it over http | Cloudflare encryption mode; X-Forwarded-Proto on your proxy; WordPress HTTPS detection |
| http:// → https:// → http:// | One layer forces https, another forces http | CDN settings such as Always Use HTTPS; server rewrite rules; WordPress addresses |
| www → no www → www | Two host rules point at each other | Domain or hosting panel; server config; CDN redirect rules; WordPress Site Address |
| /page → /page/ → /page | One rule adds a trailing slash, another removes it | Framework trailingSlash setting; vercel.json; server and CDN rules |
| /account → /login → /account | A sign-in check redirects, but the session cookie is never set or never sent back | Middleware or proxy matcher; cookie Secure flag and host |
| / → /en/ → / | Language or country rules disagree | Accept-Language and IP rules; locale cookie; CDN geo rules |
Cloudflare too many redirects: the Flexible SSL loop
This is the classic. In Cloudflare’s Flexible encryption mode, every connection from Cloudflare to your server is plain http, even when the visitor used https[11]. If your server forces https, it sees an http request and redirects to https. Cloudflare passes that redirect to a visitor who is already on https. The visitor asks again, and round it goes. Cloudflare says it directly: “If your origin forces HTTPS by automatically redirecting HTTP requests to HTTPS, do not use Flexible mode. Because Cloudflare connects to your origin over HTTP, this creates a redirect loop that makes your site inaccessible.”[11]
Vercel documents the same loop for sites on Vercel behind Cloudflare. Vercel redirects http to https itself, that redirect cannot be switched off, and the fix belongs on the Cloudflare side[15].
The fix: set the encryption mode to Full (strict), on the SSL/TLS Overview page of the Cloudflare dashboard[12]. Full (strict) connects to your server over https and checks its certificate. The certificate must be unexpired, issued by a publicly trusted authority or Cloudflare’s Origin CA, and valid for the hostname[13]. Full also uses https but does not validate the certificate[15]. Cloudflare’s other suggested fix is to remove the https redirect from your server[10], but that leaves the leg between Cloudflare and your server unencrypted. Prefer Full (strict).
Cloudflare’s troubleshooting page lists more ways to loop[10]:
- Full or Full (strict) mode while your server redirects https back to http.
- Always Use HTTPS turned on while your server redirects https back to http.
- HSTS turned on while your server sends https to http, or with the encryption mode set to Off.
- Redirect rules or Page Rules that point at each other.
Reverse proxies that drop X-Forwarded-Proto
The same loop happens without Cloudflare whenever something in front of your app ends the https connection: a load balancer, a hosting proxy, or Nginx in front of Node, PHP or Python. The app receives plain http and, if it forces https, redirects forever. Apache, for example, sets its HTTPS variable to “on” only if the connection to Apache itself uses SSL/TLS[25], and the connection from a proxy usually does not. WordPress decides with is_ssl(), which checks the server’s HTTPS flag and port 443[22].
The fix has two halves. The proxy must tell the app what the visitor used, in an X-Forwarded-Proto header; Nginx sets request headers for the app with proxy_set_header[26]. And the app must read that header instead of its own connection. Cloudflare already sends it, set to the protocol the visitor used, and overwrites any value the visitor tried to send[14]. WordPress’s HTTPS guide warns that behind a proxy that provides SSL, forcing https will “initially send any requests into an infinite redirect loop”, and shows the wp-config.php lines that fix it[21].
www and non-www pointing at each other
A host loop needs two places that each choose the main host, and choose differently. Common pairs: a www redirect in the domain or hosting panel and the opposite rule in .htaccess or nginx.conf; a CDN redirect rule against the app’s own rule; a WordPress Site Address on one host while the server forces the other. Inside WordPress, the canonical redirect sends visitors to “the proper URL based on the site url”[23], so WordPress’s idea of the host always wins there. Vercel’s advice for a loop that survives the SSL fix covers the general case: “inspect application redirects, domain redirects, and Cloudflare rules for conflicting destinations or schemes”[15].
The fix is to choose one host, set it in one layer, and make every other layer agree or stay silent. Fix the scheme and the host in a single hop, so an old http://example.com link reaches https://www.example.com in one redirect.
Too many redirects on WordPress
WordPress stores two addresses, home and siteurl. If one says http while the server forces https, or one says www while the server strips it, WordPress and the server pass the visitor back and forth. When the loop locks you out of the dashboard, set both in wp-config.php. WP_HOME overrides the stored value without changing it in the database, and each value “should include the https:// part” with no slash at the end[20].
Plugins are the other usual suspect. An SSL plugin, a redirect plugin, a cache plugin and a security plugin can each add redirects on top of the server’s and the CDN’s. Turn them off one at a time and trace again. If you cannot log in, WordPress’s own advice is to rename wp-content/plugins to plugins_old over FTP, which deactivates every plugin[24]. For sites behind Cloudflare, Cloudflare recommends its WordPress plugin with Automatic HTTPS Rewrites turned on[10].
Next.js and Vercel: redirects and trailing slashes
Next.js redirects by default when the slash is “wrong”: /about/ redirects to /about, and trailingSlash: true reverses that[18]. vercel.json has its own trailingSlash setting, which answers with a 308[16]. If two of these, or a CDN rule, disagree, the slash flips on every hop. Set it in one place.
Redirects in next.config.js run before pages and public files, and permanent: true sends a 308[17]. The docs warn that leaving out the slash before a :param in a source or destination risks infinite redirects[17]. On Vercel, the platform’s own http to https redirect cannot be switched off[15], so never add a rule of your own that points the other way. More on what crawlers receive from a Next.js site is in the Next.js SEO guide.
Login and cookie loops
Some loops only hit some visitors. The usual shape: a page checks for a session cookie, finds none, and redirects to the login page. The login page, or the step after it, redirects back. The cookie still is not there, so it repeats. Three common reasons:
- The sign-in check also runs on the login page. In Next.js, Proxy runs on every route unless a matcher excludes it[19].
- The cookie is marked Secure, but the page was reached over http. A browser sends a Secure cookie only on a secure channel[8].
- The cookie was set for one host and checked on another. A cookie set without a Domain attribute belongs to the exact host that set it[8], so
wwwand the bare domain do not share it.
// proxy.ts (called middleware.ts before Next.js 16)
import { NextResponse, type NextRequest } from "next/server";
export function proxy(request: NextRequest) {
const signedIn = request.cookies.has("session");
// Never send the login page to itself
if (!signedIn && request.nextUrl.pathname !== "/login") {
return NextResponse.redirect(new URL("/login", request.url));
}
return NextResponse.next();
}
export const config = {
// Skip static files and the login page, so sign-in can never loop
matcher: ["/((?!api|_next/static|_next/image|favicon.ico|login).*)"],
};Language and location redirects
Redirecting by language or country adds rules that can disagree. A country rule sends a US visitor to /us/, a language rule sends English readers back to /, and the pair loops. A missing locale cookie can do the same. Google advises against this kind of redirect: “Avoid automatically redirecting users from one language version of a site to a different language version of a site.” It also notes that Googlebot usually crawls from the US and sends no Accept-Language header[29]. So the fallback branch of your rules is the one Google sees. Test the page the way a crawler arrives, with no cookies and no language preference, and consider linking to the other versions instead of redirecting.
Fix it, by platform
The aim is the same everywhere. One layer owns https, one owns the host, and one owns the slash. Every old URL reaches its final address in one permanent redirect. Pick your platform.
%{HTTPS} is off for every request, because it describes Apache’s own connection[25]. The second condition reads the header the proxy sets, so a visitor who already used https is not redirected again.# .htaccess: force https once, and believe the proxy about what the visitor used
RewriteEngine On
RewriteCond %{HTTPS} off
RewriteCond %{HTTP:X-Forwarded-Proto} !https
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]proxy_set_header[26], and handle http and the bare domain in one hop. The app must then trust the header.# Nginx in front of your app, ending https: tell the app what the visitor used
server {
listen 443 ssl;
server_name www.example.com;
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
# Plain http, for either host, goes to https://www in one hop
server {
listen 80;
server_name example.com www.example.com;
return 301 https://www.example.com$request_uri;
}SSL/TLS > Overview > Encryption mode: Full (strict) Your server needs a certificate that is: - unexpired - issued by a public CA or Cloudflare Origin CA - valid for the hostname API equivalent: PATCH the zone setting "ssl" with the value "strict" Then review your Redirect Rules and Page Rules. Look for two rules whose destinations point at each other.
is_ssl() notes[22].// wp-config.php, above the require_once line at the end of the file
// The real scheme and host, with no slash at the end
define( 'WP_HOME', 'https://www.example.com' );
define( 'WP_SITEURL', 'https://www.example.com' );
// Behind a proxy or CDN that ends https before WordPress
if ( isset( $_SERVER['HTTP_X_FORWARDED_PROTO'] )
&& strpos( $_SERVER['HTTP_X_FORWARDED_PROTO'], 'https' ) !== false ) {
$_SERVER['HTTPS'] = 'on';
}// next.config.js
module.exports = {
// The default: /about/ redirects to /about. Set the slash here and
// nowhere else: no CDN rule, server rule or vercel.json doing the opposite.
trailingSlash: false,
async redirects() {
return [
// Keep the "/" before every ":param", or the rule can loop
{ source: "/old-blog/:slug", destination: "/blog/:slug", permanent: true },
];
},
};What a loop does to Google and AI crawlers
A loop is not just a visitor problem. Crawlers follow the same redirects, and a loop has no end for them either. Google’s crawlers follow up to 10 redirect hops by default[27]. Search Console’s page indexing report lists a redirect loop as a redirect error, next to chains that are too long and bad or empty redirect URLs[28]. While the loop lasts, the page cannot be crawled.
For AI crawlers the facts are thinner. We could not find a published redirect limit from OpenAI, Anthropic or Perplexity. What has been measured points the same way. Vercel’s December 2024 study of its own network found that 14.36% of the ChatGPT crawler’s fetches were spent following redirects, against 1.49% for Googlebot[30]. A crawler that runs into a loop leaves with nothing, and a page that was never read cannot be quoted or cited in an answer.
There is one more trap: a crawler can be sent down a different chain from a browser. Bot protection, CDN rules and geo rules can treat a crawler differently from you, and a page that works in your browser can loop for a bot. The Redirect Checker runs the same URL as GPTBot, ClaudeBot, PerplexityBot and Googlebot and says when the result differs. It sends each crawler’s user-agent string from our own server, not from the crawler’s addresses, so treat a difference as a lead and confirm it in your server logs. Log file analysis for AI crawlers shows how, and the AI crawler checker covers the other gate, robots.txt.
Stop the next loop before it starts
Loops come back when nobody owns the decision. Four habits prevent most of them.
One layer per decision. Decide where https is enforced, where the host is chosen, and where the trailing slash is set. Write it down. Every other layer stays silent on that question.
One hop for old URLs. Point each old address straight at its final URL, with the scheme and host fixed in the same redirect. Fewer rules leave fewer rules to disagree. Which status code to use is covered in 301 vs 302, and what a permanent redirect does for search in 301 Moved Permanently.
Test after every change. Hosting moves, CDN changes, new SSL certificates and new plugins are when loops appear. Trace the home page and one old URL each time.
Watch the whole site, not one page. A checker tests the URLs you think of. CoreCited’s Site Audit checks every page it crawls and flags the ones that redirect before they respond, so a new chain shows up in a task list rather than in a visitor’s error page.
Questions people ask
What does ERR_TOO_MANY_REDIRECTS mean?
It is Chrome's error for a redirect loop. The page sent the browser to another address, which sent it on again, until Chrome had followed 20 redirects and gave up. In nearly every case the same two or three URLs were repeating, because two rules on the site disagree, for example one forcing https and another undoing it.
How do I fix too many redirects in Chrome?
As a visitor: open the page in a private window, delete that one site's cookies (Chrome's own error page suggests it), try another browser or network, and check your device's date and time. If none of that works, the loop is on the site, and only its owner can fix it.
Why does Safari say too many redirects occurred?
It is the same problem with a different message. Apple's support article says it can happen when a page redirects to another page that redirects back to the original, and that outdated data in Safari's cache or cookies can also cause it. Remove the site's data under Safari's Privacy settings, in Manage Website Data, then try again.
How do I fix too many redirects on WordPress?
Check that the WordPress Address and Site Address use the same scheme and host as your server and CDN. If you cannot log in, set WP_HOME and WP_SITEURL in wp-config.php. Behind a proxy or CDN that ends https, add the X-Forwarded-Proto check from WordPress's HTTPS guide. Then rule out plugins by turning them off one at a time.
Why does Cloudflare cause too many redirects?
Usually because the encryption mode is Flexible. Cloudflare then connects to your server over plain http, your server redirects to https, and the visitor, who is already on https, is sent round again. Set the mode to Full (strict), with a valid certificate on the server. Always Use HTTPS and conflicting redirect rules can also cause a loop.
How many redirects is too many?
Chrome and Firefox give up after 20, and Google's crawlers follow up to 10 hops by default. But aim for one: an old URL should reach its final address in a single permanent redirect. Every extra hop is another request that can fail, for people and for crawlers.
Does a redirect loop hurt SEO and AI visibility?
While it lasts, the page cannot be reached at all. Google lists a redirect loop as a redirect error in Search Console's page indexing report, and an AI crawler that hits a loop gets no page to read or cite. No AI company publishes how many redirects its crawler follows, so do not count on a long chain working either.
