Search code examples
.htaccesshttp-redirecthttp-status-code-404http-status-code-500

.htaccess displays 500 internal server error instead of expected 404 error


I have a problem with my .htacces code, because it produces an 500 internal server error instead of a 404 error, like it should.

The 500 internal error is only caused if I try to open a page in a directory which is actually a file. For example, in the root of my website, there is the file biography.php. https://example.com/biography.php redirects to https://example.com/biography/.

But when I try to open the non-existing page https://example.com/biography/test/, it shows an 500 internal server error instead of the expected 404 error.

This is my .htacces code. The last 6 lines seem to cause the issue, because without them, the expected 404 error is displayed instead of the 500 internal server error. But without them, https://example.com/biography.php doesn't redirect to https://example.com/biography/

Maybe there is a redirect loop or something like that? I just copied the code from the internet, so I'm not able to troubleshoot it myself.

Options -Indexes
AddDefaultCharset UTF-8
ServerSignature Off
DirectoryIndex index.php

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

RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule !(^$|\.[a-zA-Z0-9]{1,5}|/)$ %{REQUEST_URI}/ [R=301,L]

RewriteCond %{DOCUMENT_ROOT}/$1.php -f
RewriteRule ^((/?[^/]+){1,2})/$ $1.php [L]

RewriteCond %{ENV:REDIRECT_STATUS} ^$
RewriteCond %{THE_REQUEST} ^[A-Z]{3,}\s([^.]+)\.php [NC]
RewriteRule ^ %1 [R=301]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}.php -f
RewriteRule ^(.*?)/?$ $1.php [NC,L]

Thank you for your help! I hope you can understand my issue! I appreciate it! :)


Solution

  • RewriteCond %{REQUEST_FILENAME} !-d
    RewriteCond %{REQUEST_FILENAME}.php -f
    RewriteRule ^(.*?)/?$ $1.php [NC,L]
    

    The rewrite-loop (which causes the 500 error) is caused by the last rule (3 directives) because the condition that checks %{REQUEST_FILENAME}.php -f is not necessarily checking the same file that you are rewriting to in the RewriteRule substitution string (ie. $1.php).

    When you request /biography/test/ you end up checking whether /biography.php exists (which it does), but you end up rewriting the request to /biography/test.php (which does not exist) - which causes a rewrite loop.

    See my answer to the following question on ServerFault for a more detailed explanation: https://serverfault.com/questions/989333/using-apache-rewrite-rules-in-htaccess-to-remove-html-causing-a-500-error

    The last rule should instead be written like this:

    # Rewrite to append the ".php" extension as required.
    RewriteCond %{DOCUMENT_ROOT}/$1.php -f
    RewriteRule ^(.*?)/?$ $1.php [L]
    

    This now checks that the target file exists before appending the .php extension to the same URL-path.

    There's no need for two conditions. And the NC flag is not required here.

    RewriteCond %{ENV:REDIRECT_STATUS} ^$
    RewriteCond %{THE_REQUEST} ^[A-Z]{3,}\s([^.]+)\.php [NC]
    RewriteRule ^ %1 [R=301]
    

    However, your redirect to remove the .php extension is not strictly correct either. This will also erroneously remove the .php "extension" should it appear in the query string part of the URL. And you are missing the L flag. You also don't need both conditions.

    Use something like the following instead:

    # Remove ".php" extension from requested URL-path
    RewriteCond %{ENV:REDIRECT_STATUS} ^$
    RewriteRule ^(.+)\.php$ /$1 [R=301,L]
    

    Summary

    # Remove ".php" extension from requested URL-path
    RewriteCond %{ENV:REDIRECT_STATUS} ^$
    RewriteRule ^(.+)\.php$ /$1 [R=301,L]
    
    # Rewrite to append the ".php" extension as required.
    RewriteCond %{DOCUMENT_ROOT}/$1.php -f
    RewriteRule ^(.*?)/?$ $1.php [L]