{"id":7,"date":"2026-07-14T00:00:00","date_gmt":"2026-07-14T00:00:00","guid":{"rendered":"http:\/\/\/var\/www\/html\/?p=7"},"modified":"2026-07-21T01:02:30","modified_gmt":"2026-07-21T01:02:30","slug":"apache-htaccess-redirect-loop-500-internal-server-error-on-ubuntu-2004-lts","status":"publish","type":"post","link":"https:\/\/butitworkedlocal.com\/apache-htaccess-redirect-loop-500-internal-server-error-on-ubuntu-2004-lts\/","title":{"rendered":"Resolving Apache htaccess Redirect Loop 500 Internal Server Error on Ubuntu 20.04 LTS"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>Troubleshoot and fix Apache 500 Internal Server Errors caused by infinite redirect loops in .htaccess files on Ubuntu 20.04 LTS servers.<\/strong><\/p>\n\n\n<p>When managing web servers, few issues are as frustrating and immediately impactful as a &quot;500 Internal Server Error&quot; due to an Apache <code>.htaccess<\/code> redirect loop. This common problem indicates that your Apache server, specifically its <code>mod_rewrite<\/code> module, is caught in an endless cycle of redirecting requests, eventually hitting an internal limit and failing. This guide provides a highly technical, step-by-step approach to diagnose and resolve this issue on an Ubuntu 20.04 LTS server running Apache 2.4.<\/p>\n<h3>Symptom &amp; Error Signature<\/h3>\n<p>Users attempting to access your website will typically encounter one of the following:<\/p>\n<ul>\n<li><p><strong>Browser Output (Common):<\/strong><\/p>\n<ul>\n<li>&quot;500 Internal Server Error&quot;<\/li>\n<li>&quot;ERR_TOO_MANY_REDIRECTS&quot; (e.g., in Chrome)<\/li>\n<li>&quot;The page isn&#039;t redirecting properly&quot; (e.g., in Firefox)<\/li>\n<li>A blank white page, sometimes after a long loading time.<\/li>\n<\/ul>\n<\/li>\n<li><p><strong>Apache Error Log Output (<code>\/var\/log\/apache2\/error.log<\/code>):<\/strong>\nYou will likely see recurring entries indicating a redirect limit being exceeded. The exact message may vary slightly but will be similar to this:<\/p>\n<pre><code>[Timestamp] [proxy:error] [pid XXXX:tid YYYYYYYYYYY] (X) [client IP_ADDRESS:PORT] AH01144: Request exceeded the limit of 10 internal redirects due to probable configuration error. Use &#039;LimitInternalRecursion&#039; to increase the limit if necessary. Use &#039;LogLevel debug&#039; to get a backtrace.\n[Timestamp] [core:crit] [pid XXXX:tid YYYYYYYYYYY] [client IP_ADDRESS:PORT] AH00124: Request exceeded the limit of 10 internal redirects due to probable configuration error. Use &#039;LimitInternalRecursion&#039; to increase the limit if necessary. Use &#039;LogLevel debug&#039; to get a backtrace.\n<\/code><\/pre>\n<p>You might also see <code>mod_rewrite<\/code> related debug messages if <code>LogLevel<\/code> is increased:<\/p>\n<pre><code>[Timestamp] [rewrite:trace1] [pid XXXX:tid YYYYYYYYYYY] [client IP_ADDRESS:PORT] AH00666: [client IP_ADDRESS:PORT] (3) [main\/htaccess] applying pattern &#039;^$&#039; to uri &#039;\/index.php&#039;\n[Timestamp] [rewrite:trace1] [pid XXXX:tid YYYYYYYYYYY] [client IP_ADDRESS:PORT] AH00669: [client IP_ADDRESS:PORT] (3) [main\/htaccess] rewrite &#039;\/index.php&#039; -&gt; &#039;\/index.php&#039;\n<\/code><\/pre>\n<p>(Note: The <code>AH01144<\/code> and <code>AH00124<\/code> errors are critical indicators of a redirect loop.)<\/p>\n<\/li>\n<\/ul>\n<h3>Root Cause Analysis<\/h3>\n<p>A 500 Internal Server Error caused by a redirect loop in <code>.htaccess<\/code> primarily stems from <code>mod_rewrite<\/code> rules that inadvertently create an infinite chain of redirects or rewrites. The underlying reasons typically fall into these categories:<\/p>\n<ol>\n<li><p><strong>Infinite Rewrite Loop:<\/strong> The most common culprit. A set of <code>RewriteRule<\/code> or <code>RewriteCond<\/code> directives are configured such that a request, after being processed by one rule, is then matched again by the same rule or another rule that effectively sends it back to a state where the first rule applies again. This cycle continues until Apache&#039;s internal redirect limit (defaulting to 10 for internal rewrites) is exceeded, leading to the 500 error.<\/p>\n<\/li>\n<li><p><strong>Incorrect <code>RewriteBase<\/code> Directive:<\/strong> When <code>.htaccess<\/code> files are used in subdirectories, <code>RewriteBase<\/code> is crucial for correctly calculating the path for internal rewrites. An incorrectly set or missing <code>RewriteBase<\/code> can cause <code>mod_rewrite<\/code> to append the subdirectory path repeatedly, leading to an infinite loop as the URI keeps growing.<\/p>\n<\/li>\n<li><p><strong>Missing or Misplaced <code>[L]<\/code> (Last) Flag:<\/strong> The <code>[L]<\/code> flag tells <code>mod_rewrite<\/code> to stop processing further <code>RewriteRule<\/code> directives for the current request after a match. If this flag is omitted where needed, subsequent rules might re-evaluate the already rewritten URI, potentially sending it into a loop.<\/p>\n<\/li>\n<li><p><strong>Conflicting or Poorly Ordered Rules:<\/strong> When combining multiple redirect types (e.g., HTTP to HTTPS, www to non-www, adding\/removing trailing slashes), the order of the rules is paramount. If, for instance, a non-www to www rule redirects to HTTP, and an HTTP to HTTPS rule then redirects to HTTPS, but doesn&#039;t correctly handle the www\/non-www part, a loop can form.<\/p>\n<\/li>\n<li><p><strong>Proxy\/Load Balancer Awareness:<\/strong> If Apache is behind a reverse proxy or load balancer (e.g., Nginx, AWS ELB\/ALB), the <code>mod_rewrite<\/code> rules for HTTPS detection (<code>RewriteCond %{HTTPS} off<\/code>) might fail. The proxy terminates SSL and forwards requests to Apache via HTTP, making <code>%{HTTPS}<\/code> always appear <code>off<\/code>. This causes Apache to continually redirect to HTTPS, even if the user is already on HTTPS externally, creating a loop. The <code>X-Forwarded-Proto<\/code> header is crucial here.<\/p>\n<\/li>\n<li><p><strong>Syntactic Errors in <code>mod_rewrite<\/code> Directives:<\/strong> While less common for a <em>redirect loop<\/em> and more for a general 500 error, malformed regular expressions or incorrect <code>mod_rewrite<\/code> syntax can lead to unpredictable behavior, including loops if the engine misinterprets the rules.<\/p>\n<\/li>\n<\/ol>\n<h3>Step-by-Step Resolution<\/h3>\n<p>Follow these steps meticulously to diagnose and resolve your Apache <code>.htaccess<\/code> redirect loop.<\/p>\n<h4>1. Verify <code>mod_rewrite<\/code> is Enabled and <code>AllowOverride<\/code> is Set<\/h4>\n<p>First, ensure the <code>mod_rewrite<\/code> module is enabled and that Apache is configured to allow <code>.htaccess<\/code> overrides for your web directory.<\/p>\n<ol>\n<li><p><strong>Check <code>mod_rewrite<\/code> status:<\/strong><\/p>\n<pre><code class=\"language-bash\">sudo apache2ctl -M | grep rewrite\n<\/code><\/pre>\n<p>If <code>rewrite_module<\/code> is not listed, enable it:<\/p>\n<pre><code class=\"language-bash\">sudo a2enmod rewrite\nsudo systemctl restart apache2\n<\/code><\/pre>\n<\/li>\n<li><p><strong>Check <code>AllowOverride<\/code> directive:<\/strong>\nThe Apache configuration for your virtual host or directory must permit <code>.htaccess<\/code> overrides. Open your virtual host configuration file, typically located at <code>\/etc\/apache2\/sites-available\/your-domain.conf<\/code> or the main Apache configuration <code>\/etc\/apache2\/apache2.conf<\/code>.\nLocate the <code>&lt;Directory&gt;<\/code> block for your web root (e.g., <code>\/var\/www\/html\/<\/code>):<\/p>\n<pre><code class=\"language-apacheconf\">&lt;Directory \/var\/www\/html\/&gt;\n    Options Indexes FollowSymLinks\n    AllowOverride All # This MUST be All\n    Require all granted\n&lt;\/Directory&gt;\n<\/code><\/pre>\n<blockquote class=\"important\"><p><code>AllowOverride All<\/code> is crucial for <code>.htaccess<\/code> files to be processed. If it&#039;s set to <code>None<\/code> or not explicitly defined, your <code>.htaccess<\/code> rules will be ignored or could lead to other issues. After modification, restart Apache:<\/p>\n<\/blockquote>\n<pre><code class=\"language-bash\">sudo systemctl restart apache2\n<\/code><\/pre>\n<\/li>\n<\/ol>\n<h4>2. Inspect Apache Error Logs for Clues<\/h4>\n<p>The Apache error log is your primary source of diagnostic information. It will often explicitly state that a redirect limit has been exceeded.<\/p>\n<ol>\n<li><strong>Monitor the error log in real-time:<\/strong><pre><code class=\"language-bash\">sudo tail -f \/var\/log\/apache2\/error.log\n<\/code><\/pre>\n<\/li>\n<li><strong>Attempt to access the problematic URL<\/strong> in your browser.<\/li>\n<li><strong>Observe the log output.<\/strong> Look for messages similar to <code>Request exceeded the limit of 10 internal redirects<\/code>. This confirms a rewrite loop. The log might also indicate the specific path being repeatedly rewritten.<\/li>\n<\/ol>\n<h4>3. Temporarily Disable the <code>.htaccess<\/code> File<\/h4>\n<p>To confirm that the <code>.htaccess<\/code> file is indeed the source of the problem, temporarily disable it.<\/p>\n<ol>\n<li><strong>Navigate to your website&#039;s document root:<\/strong><pre><code class=\"language-bash\">cd \/var\/www\/html\/your-domain.com\/public_html\/ # Adjust path as necessary\n<\/code><\/pre>\n<\/li>\n<li><strong>Rename the <code>.htaccess<\/code> file:<\/strong><pre><code class=\"language-bash\">mv .htaccess .htaccess_bak\n<\/code><\/pre>\n<\/li>\n<li><strong>Clear your browser cache<\/strong> (Ctrl+Shift+R or Cmd+Shift+R) and try accessing your site again.<ul>\n<li>If the site now loads (even without intended redirects), then the <code>.htaccess<\/code> file is definitely the cause.<\/li>\n<li>If the site still shows a 500 error or doesn&#039;t load, the issue might be elsewhere (e.g., PHP error, server misconfiguration), but for this guide, we assume the <code>.htaccess<\/code> is the culprit.<\/li>\n<\/ul>\n<\/li>\n<li><strong>Once confirmed, proceed to analyze the <code>.htaccess<\/code> contents.<\/strong> You can rename it back: <code>mv .htaccess_bak .htaccess<\/code> before proceeding to the next step, or work on <code>.htaccess_bak<\/code> directly and then rename it.<\/li>\n<\/ol>\n<h4>4. Analyze and Correct <code>.htaccess<\/code> Rules<\/h4>\n<p>This is the most critical step. You&#039;ll need to carefully review your <code>.htaccess<\/code> file for common misconfigurations that lead to redirect loops.<\/p>\n<blockquote class=\"important\"><p><strong>Always back up your <code>.htaccess<\/code> file<\/strong> before making any changes. A simple <code>cp .htaccess .htaccess_backup_$(date +%Y%m%d%H%M%S)<\/code> will suffice.<\/p>\n<\/blockquote>\n<p>Open your <code>.htaccess<\/code> file for editing:<\/p>\n<pre><code class=\"language-bash\">nano .htaccess # Or your preferred editor\n<\/code><\/pre>\n<p>Focus on <code>RewriteEngine On<\/code>, <code>RewriteCond<\/code>, and <code>RewriteRule<\/code> directives.<\/p>\n<p><strong>Common Pitfalls and Solutions:<\/strong><\/p>\n<h5>a. HTTP to HTTPS Redirect Loop (Especially with Load Balancers\/Proxies)<\/h5>\n<p>This is a very common loop, often occurring when Apache is behind a proxy that handles SSL termination. Apache then sees requests as HTTP, and the <code>.htaccess<\/code> repeatedly tries to redirect to HTTPS.<\/p>\n<p><strong>Incorrect (Common Error):<\/strong><\/p>\n<pre><code class=\"language-apacheconf\"># This can loop if Apache sees all requests as HTTP due to a proxy\nRewriteEngine On\nRewriteCond %{HTTPS} off\nRewriteRule ^(.*)$ https:\/\/%{HTTP_HOST}%{REQUEST_URI} [L,R=301]\n<\/code><\/pre>\n<p><strong>Corrected using <code>X-Forwarded-Proto<\/code> (for proxy setups):<\/strong>\nIf your proxy sets <code>X-Forwarded-Proto<\/code> (e.g., <code>https<\/code>), use this header to detect the original protocol.<\/p>\n<pre><code class=\"language-apacheconf\">RewriteEngine On\nRewriteCond %{HTTP:X-Forwarded-Proto} !https [NC]\nRewriteRule ^ https:\/\/%{HTTP_HOST}%{REQUEST_URI} [L,R=301]\n<\/code><\/pre>\n<p>If not behind a proxy and direct SSL:<\/p>\n<pre><code class=\"language-apacheconf\">RewriteEngine On\nRewriteCond %{HTTPS} off\nRewriteRule ^ https:\/\/%{HTTP_HOST}%{REQUEST_URI} [L,R=301]\n<\/code><\/pre>\n<h5>b. WWW to Non-WWW (or vice versa) Redirect Loop<\/h5>\n<p>Ensure your www\/non-www rules are correctly ordered, especially when combined with HTTPS redirects.<\/p>\n<p><strong>Incorrect (Potential Loop\/Conflict):<\/strong><\/p>\n<pre><code class=\"language-apacheconf\"># This rule might redirect to HTTP www, then HTTPS rule kicks in, then back to HTTP www...\nRewriteEngine On\nRewriteCond %{HTTP_HOST} ^www.(.*)$ [NC]\nRewriteRule ^(.*)$ http:\/\/example.com\/$1 [L,R=301]\n<\/code><\/pre>\n<p><strong>Corrected (Non-WWW with HTTPS priority):<\/strong>\nIt&#039;s generally best to redirect to HTTPS <em>first<\/em>, then handle www\/non-www.<\/p>\n<pre><code class=\"language-apacheconf\">RewriteEngine On\n\n# 1. Redirect HTTP to HTTPS\nRewriteCond %{HTTPS} off\nRewriteRule ^ https:\/\/%{HTTP_HOST}%{REQUEST_URI} [L,R=301]\n\n# 2. Redirect WWW to Non-WWW (only if the host starts with www.)\nRewriteCond %{HTTP_HOST} ^www.(.*)$ [NC]\nRewriteRule ^ https:\/\/%1%{REQUEST_URI} [L,R=301]\n<\/code><\/pre>\n<p>In this example:<\/p>\n<ul>\n<li>The first rule ensures all traffic is HTTPS.<\/li>\n<li>The second rule <em>assumes<\/em> the traffic is already HTTPS (or will be immediately redirected by the first rule) and then handles the www part. <code>https:\/\/%1%{REQUEST_URI}<\/code> ensures it stays on HTTPS while removing <code>www.<\/code>.<\/li>\n<\/ul>\n<h5>c. Missing <code>[L]<\/code> (Last) Flag<\/h5>\n<p>Always ensure redirect rules that should terminate processing have the <code>[L]<\/code> flag. Without it, subsequent rules might apply to the already rewritten URI, leading to unintended behavior or loops.<\/p>\n<p><strong>Example:<\/strong><\/p>\n<pre><code class=\"language-apacheconf\"># Original rule potentially causing a loop if another rule re-processes the URI\nRewriteRule ^old-page.html$ \/new-page.html [R=301]\n\n# Corrected with [L] flag\nRewriteRule ^old-page.html$ \/new-page.html [L,R=301]\n<\/code><\/pre>\n<h5>d. Incorrect <code>RewriteBase<\/code><\/h5>\n<p>If your <code>.htaccess<\/code> file is in a subdirectory (e.g., <code>\/var\/www\/html\/blog\/.htaccess<\/code>), ensure <code>RewriteBase<\/code> is set correctly.<\/p>\n<p><strong>Example:<\/strong>\nFor <code>\/var\/www\/html\/blog\/.htaccess<\/code>:<\/p>\n<pre><code class=\"language-apacheconf\">RewriteEngine On\nRewriteBase \/blog\/ # Important for correct path calculation\nRewriteRule ^index.php$ - [L]\nRewriteCond %{REQUEST_FILENAME} !-f\nRewriteCond %{REQUEST_FILENAME} !-d\nRewriteRule . \/blog\/index.php [L]\n<\/code><\/pre>\n<p>If your <code>.htaccess<\/code> is in the document root (e.g., <code>\/var\/www\/html\/.htaccess<\/code>), <code>RewriteBase \/<\/code> is usually appropriate, or it can often be omitted if rules are relative to the root.<\/p>\n<h5>e. Systematically Test Rules<\/h5>\n<p>If you have many rules, comment them out one by one (or in small groups) and test after each change to isolate the problematic rule(s).<\/p>\n<pre><code class=\"language-apacheconf\"># RewriteEngine On\n# RewriteCond %{HTTPS} off\n# RewriteRule ^ https:\/\/%{HTTP_HOST}%{REQUEST_URI} [L,R=301]\n\n# This rule is currently active and being tested\nRewriteEngine On\nRewriteCond %{HTTP_HOST} ^www.(.*)$ [NC]\nRewriteRule ^ https:\/\/%1%{REQUEST_URI} [L,R=301]\n<\/code><\/pre>\n<p>After testing, uncomment other rules gradually.<\/p>\n<h4>5. Debug <code>mod_rewrite<\/code> with <code>LogLevel<\/code> (Advanced)<\/h4>\n<p>For deep debugging, you can temporarily increase Apache&#039;s <code>LogLevel<\/code> for <code>mod_rewrite<\/code>. This will produce extremely verbose output in your error log, showing how each rule is processed.<\/p>\n<ol>\n<li><p><strong>Open your main Apache configuration file<\/strong> (<code>\/etc\/apache2\/apache2.conf<\/code>) or your virtual host file (<code>\/etc\/apache2\/sites-available\/your-domain.conf<\/code>).<\/p>\n<\/li>\n<li><p><strong>Locate the <code>LogLevel<\/code> directive<\/strong> (usually <code>LogLevel warn<\/code>) and add or modify it to include <code>rewrite:traceN<\/code>, where <code>N<\/code> is a number from 1 to 6 (6 being the most verbose). For Apache 2.4, <code>trace1<\/code> through <code>trace8<\/code> are available. <code>trace5<\/code> or <code>trace6<\/code> is usually sufficient for detailed rewrite debugging.<\/p>\n<pre><code class=\"language-apacheconf\">LogLevel warn rewrite:trace5\n<\/code><\/pre>\n<blockquote class=\"warning\"><p>Enabling <code>rewrite:trace<\/code> generates an enormous amount of log data very quickly and can severely impact server performance and disk space. <strong>Do not use this on a production server for extended periods.<\/strong> Revert the <code>LogLevel<\/code> immediately after debugging.<\/p>\n<\/blockquote>\n<\/li>\n<li><p><strong>Restart Apache:<\/strong><\/p>\n<pre><code class=\"language-bash\">sudo systemctl restart apache2\n<\/code><\/pre>\n<\/li>\n<li><p><strong>Access the problematic URL.<\/strong><\/p>\n<\/li>\n<li><p><strong>Monitor the error log<\/strong> (<code>sudo tail -f \/var\/log\/apache2\/error.log<\/code>). You will see detailed information about how <code>mod_rewrite<\/code> processes each condition and rule. Look for patterns where a URI is repeatedly matched and rewritten by the same or a cyclical set of rules.<\/p>\n<p>Example log snippet you might see during a loop:<\/p>\n<pre><code>[rewrite:trace2] [client 127.0.0.1:40830] rewrite &#039;index.php&#039; -&gt; &#039;index.php&#039;\n[rewrite:trace2] [client 127.0.0.1:40830] add path info and hook &#039;index.php&#039; to URI\n[rewrite:trace2] [client 127.0.0.1:40830] strip per-dir prefix: \/var\/www\/html\/index.php -&gt; index.php\n[rewrite:trace2] [client 127.0.0.1:40830] applying pattern &#039;^$&#039; to uri &#039;index.php&#039;\n[rewrite:trace2] [client 127.0.0.1:40830] rewrite &#039;index.php&#039; -&gt; &#039;index.php&#039;\n[rewrite:trace2] [client 127.0.0.1:40830] add path info and hook &#039;index.php&#039; to URI\n... repeated entries indicating a loop ...\n<\/code><\/pre>\n<\/li>\n<li><p><strong>Once debugging is complete, revert the <code>LogLevel<\/code><\/strong> to its original value (e.g., <code>LogLevel warn<\/code>) and restart Apache.<\/p>\n<\/li>\n<\/ol>\n<h4>6. Restore Configuration and Test<\/h4>\n<p>After implementing your corrected <code>.htaccess<\/code> rules:<\/p>\n<ol>\n<li><strong>Ensure <code>LogLevel<\/code> is reset<\/strong> in <code>apache2.conf<\/code> or your virtual host file.<\/li>\n<li><strong>Rename your <code>.htaccess_bak<\/code> (if you used it for editing) back to <code>.htaccess<\/code>:<\/strong><pre><code class=\"language-bash\">mv .htaccess_bak .htaccess\n<\/code><\/pre>\n<\/li>\n<li><strong>Restart Apache:<\/strong><pre><code class=\"language-bash\">sudo systemctl restart apache2\n<\/code><\/pre>\n<\/li>\n<li><strong>Clear your browser cache<\/strong> and test your website thoroughly, including different URLs and sections.<\/li>\n<\/ol>\n<p>By meticulously following these steps, you should be able to pinpoint and resolve the Apache <code>.htaccess<\/code> redirect loop causing your 500 Internal Server Error on Ubuntu 20.04 LTS.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Troubleshoot and fix Apache 500 Internal Server Errors caused by infinite redirect loops in .htaccess files on Ubuntu 20.04 LTS servers.<\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[2],"tags":[16,3,14,17,15,18],"class_list":["post-7","post","type-post","status-publish","format-standard","hentry","category-web-server","tag-500-internal-server-error","tag-apache","tag-htaccess","tag-mod_rewrite","tag-redirect-loop","tag-ubuntu-20-04"],"_links":{"self":[{"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/posts\/7","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/comments?post=7"}],"version-history":[{"count":2,"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/posts\/7\/revisions"}],"predecessor-version":[{"id":255,"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/posts\/7\/revisions\/255"}],"wp:attachment":[{"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/media?parent=7"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/categories?post=7"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/butitworkedlocal.com\/wp-json\/wp\/v2\/tags?post=7"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}