Changing a web page’s URL is risky. Every link pointing to the old address breaks, and search engines lose track of the page entirely. NGINX’s redirection rules solve this: when a URL changes, a rule issues an HTTP 301 Moved Permanently response and points visitors and crawlers to the new address.
Problems start when a redirect rule ends up pointing back to itself, whether directly or through a chain of other rules. The result is a redirect loop: the URL keeps resolving to itself (or cycling through a set of URLs) and never actually loads. This article walks through how to detect a redirect loop, why it happens, and how to fix it.
Note: This article assumes NGINX is already installed on your Kamatera Ubuntu instance. For installation help, see How to Install NGINX on Your Ubuntu 22.04 Host Server and Setting Up a LEMP Stack on Kamatera for Web Development.
How NGINX redirects URLs
Redirection rules live in NGINX’s main configuration file. To edit it, connect to your cloud server through the Kamatera console or a local SSH session, then open the file:
nano /etc/nginx/nginx.conf
NGINX offers two directives for redirecting URLs: return and rewrite.
“Return” sends visitors to one specific, fixed address. It’s predictable but not flexible:
location = /products/new-products {
return 301 $scheme://mysite.com/products/new;
}
“Rewrite” transforms the URL using regular expressions, so it can match and redirect patterns dynamically:
rewrite ^/(.*)$ /prod/$1
That flexibility comes at a cost. Because rewrite matches patterns rather than one exact URL, it’s easy to write a rule that unintentionally matches its own output. Misconfigured rewrite directives are the most common source of redirect loops in NGINX.
Detecting a redirect loop
Here are three methods you can use to detect redirect loops.
Note: NGINX limits retries to 10 for internal redirect/rewrite cycles (when rewrite reprocesses a request internally without sending anything back to the client). Instead of getting stuck in an infinite loop, it returns a 500 error.
In the browser
Open the redirected page in your browser. If it won’t load, check your browser’s developer console for ERR_TOO_MANY_REDIRECTS. This means the browser gave up after following too many redirects in a row.
Using curl
curl is a command line tool that sends a request to a URL and shows you the response. Use this command to trace a redirect chain and check whether it loops:
curl -IL --max-redirs <number> <url>
-I returns the response’s HTTP headers and status code
-L follows each redirect and prints every step, so you can see where the loop happens
–max-redirs <number> caps how many redirects curl will follow, so the command doesn’t hang indefinitely
<url> is the address you’re testing
For example:
curl -IL --max-redirs 10 https://mysite.com HTTP/1.1 301 Moved Permanently Location: https://mysite.com/ HTTP/2 301 Location: http://mysite.com/ HTTP/1.1 301 Moved Permanently Location: https://mysite.com/
This output shows the URL bouncing between https://mysite.com/ and http://mysite.com/ instead of resolving, which confirms a loop.
NGINX error log
NGINX logs errors to a file on the server. Open it directly or use tail to see the most recent entries:
sudo tail -f /var/log/nginx/error.log
Why it happens
Before we can fix the problem, we need to know why it happens. Here are four common causes of redirect loops.
Incorrect rule order
NGINX processes rewrite rules in strict order. First, it executes rules that redirect to local server files. Next, it selects the best matching location block. Then it rewrites the directives in that block. Any directive with a last flag triggers a new location lookup.
Within the selected rule block, each redirect rule is processed based on its position. Placing a highly specific rule before a general one can trigger a loop, because the modified specific URL no longer exists and can’t be matched.
rewrite ^/(.*)$ /product/$1 rewrite ^/(.*)$ /prod/$1
Missing and improperly used flags
An NGINX rewrite rule matches the old URL path and redirects it to the new one. For example:
rewrite ^/old-page$ /new-page
If the rule doesn’t include the last flag, NGINX keeps trying to reach the page up to 10 times:
rewrite ^/old-page$ /new-page last
The last flag initiates a new location search with the rewritten URI. In many cases, though, the last flag can make things worse and even trigger an infinite loop:
rewrite ^/(.*)$ /product/$1 last;
Here, instead of rewriting the URL to /product/…, it rewrites it to /product/product/…, triggering a new redirect loop.
RegEx issues
Support for regular expressions makes rewrites highly flexible, but writing an effective regex is difficult and often takes trial and error. For example, if your regex is missing an anchor (^ or $), it will return a long list of potential matches instead of the single URL you’re looking for:
rewrite /about /about-us permanent;
Typos and capitalization are other common reasons regexes fail or produce unreliable results.
Reverse proxy mismatches
A reverse proxy isolates users and devices on an internal network from external networks. It receives requests from an external source and relays them to an internal server. NGINX’s proxy directive rewrites the original URL to an internal address:
proxy_redirect http://backend:8080/ /;
A mismatch between secure HTTPS URLs and insecure HTTP URLs can trigger redirect loops.
How to fix redirect loops
Now that we know how to detect the problem and what causes it, we can fix it.
Incorrect rule order
Reorder the rules so general rules are processed before specific rules:
rewrite ^/product/(.*)$ /prod/$1; (specific rule) rewrite ^/(.*)$ /default/$1; (general rule)
Missing and improperly used flags
Rewrite directive flags trigger redirect loops when the evaluated expression produces an ambiguous result and the last flag initiates a new location search. A good way to ensure an unambiguous result is to wrap the rewrite in a condition that prevents it from rewriting an already-matched result:
if ($uri !~ ^/product/) {
rewrite ^/(.*)$ /product/$1 last;
}
RegEx issues
Support for regular expressions makes rewrites highly flexible, but writing an effective regex is difficult and often takes trial and error. Check your regular expression for case sensitivity and typos, then use anchor characters to match an exact pattern. For example, replace:
rewrite /about /about-us permanent;
With the following, which uses the ^ and $ anchors to isolate the word “about” and return an exact match:
rewrite ^/about$ /about-us permanent;
Reverse proxy mismatches
To fix the issue, configure the proxy’s HTTP header:
# Sets the header to the protocol used by the client (http or https) proxy_set_header X-Forwarded-Proto $scheme; proxy_pass http://backend:8080; proxy_set_header Host $host; proxy_set_header X-Real-IP 83.299.21.10; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
Conclusion
In an ideal world, web page names and URLs would remain constant and only change in extreme circumstances. In practice, page names, locations, and URLs change constantly for legitimate reasons. NGINX’s “return” and “rewrite” directives redirect pages when you need to rename or relocate them. When configured correctly, redirects work smoothly and transparently behind the scenes. When they fail, everyone notices, and the consequences are significant: visitors can’t find the page, and search engines can’t crawl the site, limiting its discoverability. Ultimately, this damages the site owner’s trust and reputation.
In this article, we looked at what triggers redirect loops on NGINX, how to detect them, what causes them, and how to fix them. The good news is that redirect loops are easy to detect, have identifiable causes, and are simple to fix. Once you find the problem on your own site, you can resolve it quickly before it causes any real damage to you or your organization.




