Resolving Apache .htaccess Redirect Loop 500 Internal Server Error

Fix Apache .htaccess redirect loops causing 500 errors. Learn to troubleshoot mod_rewrite rules, canonical URLs, and SSL configurations to restore site access quickly.


A “500 Internal Server Error” originating from an .htaccess redirect loop is a common, yet frustrating, issue for web administrators. It typically signifies a misconfigured mod_rewrite rule that continuously redirects a request, causing the Apache server to exceed its internal redirect limit and eventually fail. This guide will walk you through diagnosing and resolving these persistent redirect loops, ensuring your website serves content correctly.

Symptom & Error Signature

When an Apache .htaccess redirect loop occurs, users will typically encounter one of the following in their web browser:

  • HTTP 500 Internal Server Error: A generic error page indicating a server-side problem.
  • ERR_TOO_MANY_REDIRECTS: Some browsers, like Chrome, explicitly report that the page isn’t redirecting properly.
  • “This page isn’t working” / “Firefox has detected that the server is redirecting the request for this address in a way that will never complete.”: Similar messages indicating a browser-level redirect limit was hit.

The definitive proof of a redirect loop resides in your Apache error logs. You’ll typically find an entry similar to this:

[Sat Jun 27 10:30:00.123456 2026] [core:error] [pid 12345:tid 123456789] [client 192.168.1.100:54321] AH00124: Request exceeded the limit of 10 internal redirects due to probable configuration error. Use 'LimitInternalRecursion' to increase the limit if necessary. Use 'LogLevel Debug' to get a backtrace.
[Sat Jun 27 10:30:00.123456 2026] [core:error] [pid 12345:tid 123456789] [client 192.168.1.100:54321] AH00124: /var/www/html/index.php pcfg_openfile: unable to check access time of /var/www/html/index.php

The AH00124 error code is the primary indicator of an internal redirect loop. The message might also point to the specific file being repeatedly requested, like index.php.

Root Cause Analysis

A redirect loop essentially means that a RewriteRule in your .htaccess file is continuously matching the URL it just rewrote, or it’s part of a cycle of two or more rules rewriting back and forth. This leads to an endless internal redirection chain until Apache’s LimitInternalRecursion (default 10) is hit, triggering the 500 error.

Common underlying reasons include:

  1. Missing or Incorrect RewriteCond (Rewrite Condition): Rules are applied indiscriminately without checking if the target condition (e.g., already HTTPS, already non-WWW) is met. This is the most frequent culprit.
  2. Conflicting RewriteRule Directives: Two or more rules that contradict each other, leading to an infinite cycle (e.g., A redirects to B, and B redirects back to A).
  3. Improper Use of Flags ([L], [R], [NC], [OR]):
    • Lack of [L] (Last): Allows Apache to continue processing subsequent rules even after a match, potentially re-matching the same rule or a conflicting one.
    • Incorrect [R] (Redirect): Causes an external HTTP redirect instead of an internal rewrite, which can also loop if not properly conditioned.
  4. RewriteBase Misconfiguration: In subdirectory installations, an incorrect RewriteBase can lead to URI mismatches, causing rules to re-evaluate incorrectly.
  5. Environment Mismatches (e.g., SSL offloading with proxies): When your website is behind a reverse proxy or load balancer that handles SSL, Apache might not see %{HTTPS} as on. If you then force HTTPS based on %{HTTPS}, it will loop.
  6. Caching Issues: Less common for a 500 error, but browser or CDN caches can sometimes store faulty redirects, exacerbating testing.
  7. AllowOverride Directive: While usually resulting in a 403 Forbidden or rules not being applied, if AllowOverride is set to None and an old browser cache still tries to apply a redirect, it could contribute to confusion. However, for a 500 AH00124, the .htaccess file is being read and processed.

Step-by-Step Resolution

Follow these steps to diagnose and resolve the Apache .htaccess redirect loop.

1. Backup Your .htaccess File

[!IMPORTANT] Before making any changes, always back up your current .htaccess file. This allows you to revert to the original state if things go wrong or if you make an incorrect modification.

You can do this via SSH:

sudo cp /var/www/html/.htaccess /var/www/html/.htaccess.backup_$(date +%Y%m%d%H%M%S)

2. Locate and Examine Apache Error Logs

The Apache error logs are your primary source of information. On Debian/Ubuntu systems, they are typically found here:

sudo tail -f /var/log/apache2/error.log

Keep this terminal window open while you attempt to access your website. Look for the AH00124 error message. It may provide clues about the specific URL or file that’s being repeatedly accessed.

3. Temporarily Disable mod_rewrite or Comment Out Rules

To quickly confirm if mod_rewrite is indeed the cause, you can temporarily disable it or comment out all RewriteRule and RewriteCond directives.

  1. Disable mod_rewrite (less recommended for .htaccess issues): This requires root access and will affect all mod_rewrite rules on the server.

    sudo a2dismod rewrite
    sudo systemctl restart apache2

    If your site loads (without redirects, potentially looking broken), mod_rewrite was the issue. Re-enable with sudo a2enmod rewrite after troubleshooting.

  2. Comment out .htaccess rules (recommended): Edit your .htaccess file (e.g., nano /var/www/html/.htaccess) and place a # at the beginning of every RewriteRule and RewriteCond line.

    #RewriteEngine On
    #RewriteCond %{HTTPS} off
    #RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
    #RewriteCond %{HTTP_HOST} ^www\.example\.com [NC]
    #RewriteRule ^(.*)$ https://example.com/$1 [L,R=301]

    Save the file and try accessing your site. If the 500 error disappears, the issue is definitely within your mod_rewrite rules. Re-enable rules one by one or in small groups to pinpoint the problematic one.

4. Analyze and Correct Common Redirect Loop Scenarios

Once you’ve identified the problematic rule (or set of rules), apply the following common fixes:

Scenario A: HTTP to HTTPS Redirect Loop

This is often caused by the RewriteCond %{HTTPS} off not correctly detecting HTTPS when behind a reverse proxy or load balancer.

Problematic Example:

RewriteEngine On
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

If your server is behind a proxy that terminates SSL, Apache might still see requests as HTTP (%{HTTPS} is off), even though the user connected via HTTPS. The proxy typically sets an X-Forwarded-Proto header.

Solution: Check for X-Forwarded-Proto header.

# Correct HTTP to HTTPS redirect, handling proxies
RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Proto} !https [NC]
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]

The [NC] flag makes the condition case-insensitive. Adding %{HTTPS} off as an OR condition, or as a fallback, is also a robust approach for environments without proxies.

Scenario B: WWW to Non-WWW (or vice-versa) Redirect Loop

Loops can occur when combined with HTTPS redirects, or if the rule isn’t specific enough.

Problematic Example (forcing non-WWW):

RewriteEngine On
# Force non-WWW (might loop with HTTPS or other rules)
RewriteCond %{HTTP_HOST} ^www\.example\.com [NC]
RewriteRule ^(.*)$ http://example.com/$1 [L,R=301]

If you have a separate HTTP to HTTPS rule and then this one, they might conflict, e.g., https://www.example.com -> http://example.com (by this rule) -> https://example.com (by HTTPS rule), leading to a loop.

Solution: Combine redirects and ensure conditions prevent self-matching.

# Combined HTTPS and non-WWW redirect (canonical URL)
RewriteEngine On
RewriteCond %{HTTP:X-Forwarded-Proto} !https [NC,OR]
RewriteCond %{HTTPS} off [OR]
RewriteCond %{HTTP_HOST} ^www\.example\.com [NC]
RewriteRule ^(.*)$ https://example.com%{REQUEST_URI} [L,R=301]

This single rule ensures all requests are sent to https://example.com, removing both the www. and forcing HTTPS in one go, preventing conflicts. Change example.com to your domain.

Scenario C: Trailing Slash Issues

Redirecting URLs to add/remove trailing slashes can loop if not handled carefully, especially with DirectoryIndex or file names.

Problematic Example:

# Add trailing slash for directories (problem if target is already /)
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^(.*[^/])$ $1/ [R=301,L]

Solution: Be specific and use conditions to avoid matching already correct URLs.

# Add trailing slash if a directory AND no slash exists
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^(.*[^/])$ /$1/ [R=301,L]

# Or, remove trailing slash if not a directory (e.g. for clean URLs)
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_URI} (.+)/$
RewriteRule ^(.*)/$ $1 [R=301,L]
Scenario D: Index File Redirects (e.g., index.php removal)

If you’re rewriting /index.php to /, ensure it doesn’t then try to serve /index.php again implicitly.

Problematic Example:

RewriteRule ^(.*)/index\.php$ /$1/ [R=301,L]

If Apache’s DirectoryIndex is set to index.php, requesting / will internally map to index.php, which then matches this rule, leading to a loop.

Solution: Use conditions to avoid matching the base URL after the rewrite, or ensure your DirectoryIndex is well-defined.

# Remove index.php from URL, but prevent loop on root
RewriteCond %{THE_REQUEST} /index\.php [NC]
RewriteRule ^(.*)index\.php$ /$1 [R=301,L,NC]

%{THE_REQUEST} matches the initial request line, preventing internal rewrites from triggering the rule again.

5. Ensure [L] (Last) Flag is Used Appropriately

The [L] (Last) flag is crucial. It tells mod_rewrite to stop processing the current set of rules if the RewriteRule matches. Without it, subsequent rules in .htaccess might re-evaluate the rewritten URL, potentially leading to a loop. Ensure all final redirect rules ([R=301]) also include [L].

6. Verify AllowOverride Directive

For .htaccess files to work, the AllowOverride directive in your Apache virtual host configuration or apache2.conf must permit it. If AllowOverride is set to None for the relevant directory, your .htaccess rules will be ignored (often resulting in a 403 Forbidden or no redirect at all), but in some edge cases, it can cause unexpected behavior.

[!IMPORTANT] The AllowOverride directive is configured in your main Apache configuration files, NOT in .htaccess. Common locations are /etc/apache2/apache2.conf or in your site’s virtual host configuration file (e.g., /etc/apache2/sites-available/yourdomain.conf).

Open your virtual host configuration file:

sudo nano /etc/apache2/sites-available/yourdomain.conf

Or the main Apache config:

sudo nano /etc/apache2/apache2.conf

Look for a <Directory> block corresponding to your website’s root path (e.g., /var/www/html). Ensure AllowOverride is set to All or FileInfo. FileInfo is generally sufficient for mod_rewrite rules.

<Directory /var/www/yourwebsite>
    Options Indexes FollowSymLinks
    AllowOverride All   # Or AllowOverride FileInfo
    Require all granted
</Directory>

If you modify your main Apache configuration, you’ll need to restart Apache.

7. Test Configuration and Restart Apache

After making any changes to .htaccess or main Apache configuration, always test the syntax and restart the server.

sudo apache2ctl configtest

You should see Syntax OK. If not, review the error messages and correct them.

sudo systemctl restart apache2

8. Clear Browser Cache

[!WARNING] Web browsers aggressively cache 301 (Permanent) redirects. If you’ve been testing faulty redirect rules, your browser might have cached the bad redirect, causing it to loop even after you’ve fixed the .htaccess file.

After fixing your .htaccess file and restarting Apache, clear your browser’s cache completely, or use an incognito/private browsing window to test. You can also use curl -I example.com from the command line to see HTTP headers and verify redirects without browser caching.

curl -I https://example.com
curl -I http://www.example.com

This will show you the exact HTTP response codes (e.g., HTTP/1.1 301 Moved Permanently) and the Location header, indicating where the server is attempting to redirect.

By systematically applying these steps, you should be able to identify, correct, and resolve the Apache .htaccess redirect loop leading to a 500 Internal Server Error.