Changing a web page’s URL is risky: every existing link breaks, and search engines lose the ability to index the page. Apache’s redirection rules prevent that. Each rule flags the old URL, returns an HTTP 301 Moved Permanently response, and points to the new address. You define these rules in a .htaccess file.
A redirect loop happens when a redirected URL matches a rule that sends it right back to itself. At best, the loop eventually times out. At worst, it runs indefinitely. Here’s how to detect the problem, why it happens, and how to fix it.
Note: This article assumes that Apache is already installed on your Kamatera Ubuntu instance. For a detailed explanation of how to install Apache, see: Setting Up a LAMP Stack on Kamatera for Web Development, How to Install a LAMP Stack on Ubuntu.
Detection
Here are three ways to detect a redirect loop.
In the browser
Launch your browser and open a redirected page. If the browser can’t open the page, then you may have a redirect error.
To see if the URL triggers a redirect loop, open your browser’s developer tools. In the console, search for ERR_TOO_MANY_REDIRECTS.
Using curl
Curl is a CLI tool that sends a request to a URL and shows you the response. Here’s how to use it to detect redirect loops:
- -I returns the HTTP header and status code
- -L follows each redirect step, exposing the loop
- –max-redirs <number> caps how many redirects curl will follow
Example:
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/
Apache error log
Apache logs all errors to a file. Open it in a text editor, or use tail to view the most recent entries at the end of the file:
tail -f /var/log/apache2/error.log
Why redirect loops happen
Rule order
Rules execute in the order they’re defined. A general rule placed before a specific one can rewrite the specific rule’s target before it’s ever matched:
# General
RewriteRule ^products/(.*)$ /shop/$1 [R=301,L]
# Specific
RewriteRule ^products/old-product$ /products/new-product [R=301,L]
A loop is triggered because the modified specific URL no longer exists and can’t be matched.
File context
Say you have a small website in /var/www/html. This directory holds all the HTML pages that resolve to https://mysite, along with your .htaccess files. As the site grows, you group related pages into subdirectories. For example, all product pages are stored in /var/www/html/products, which resolves to https://mysite/products.
To keep redirect rules for that subdirectory manageable, you add a separate .htaccess file inside it. But when a rule inside that file starts with the ^/ pattern, Apache strips out the subdirectory name before matching:
RewriteRule ^/myproduct$ /products
The result: index.html still resolves correctly to the site’s home page, but other pages like products.html or product.html can no longer be located, which triggers a redirect loop.
URL canonicalization
A canonical URL has exactly one form, typically the www-prefixed, HTTPS version. Migrating in stages, for example HTTP to HTTPS first, then non-www to www, means multiple redirect rules firing in sequence. If any one of them points back to a version of the URL that no longer resolves, it loops:
RewriteEngine On
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ https://www.yourdomain.com%{REQUEST_URI} [L,R=301,NC]
Misplaced [L] flag
In .htaccess, the [L] flag doesn’t stop rule processing outright. It tells Apache to restart matching from the top of the ruleset using the newly rewritten URL. If that URL matches the same rule again, it loops indefinitely:
RewriteRule /myproducts /products [L]
How to fix redirect loops
Now that we know how to detect the problem and what causes it, we can fix it.
Rule order
Move specific rules above general ones:
# Specific RewriteRule ^products/old-product$ /products/new-product [R=301,L] # General RewriteRule ^products/(.*)$ /shop/$1 [R=301,L]
Rule location and context
When a rule starts with the ^/ pattern, the subdirectory name is stripped out. To prevent this from happening, do not include this pattern in the path; for example, replace:
RewriteRule ^/myproduct$ /products
with
RewriteRule ^/myproduct$ /products
URL canonicalization
Combine your conditions into a single rule so the server resolves protocol and domain in one step, rather than redirecting through several intermediate URLs:
Misplaced [L] flag
Replace [L] with [END]. Unlike [L], [END] stops rule processing immediately rather than restarting it:
RewriteRule /myproducts /products [END]
Conclusion
Redirect loops happen because rule sets are easy to build up over time and hard to keep consistent. The fix is usually small: reorder a rule, drop a leading slash, or swap a flag. Catching it early keeps your pages indexable and your links working.



