I defined this .htaccess file:
RewriteEngine On
# Disable http
RewriteCond %{HTTPS} off
RewriteRule ^(.*)$ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
# Disable www.
RewriteCond %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule ^(.*)$ https://%1/$1 [R=301,L]
# /random run random.php
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^([^\.]+)$ $1.php [NC,L]
# Redirect /index.html in URL to /
RewriteRule (.*)index\.html$ /$1 [R=301,L]
# Redirect xxx.html in URL to nach xxx
RewriteCond %{THE_REQUEST} /([^.]+)\.html [NC]
RewriteRule ^ /%1 [NC,L,R]
RewriteCond %{REQUEST_FILENAME}.html -f
RewriteRule ^ %{REQUEST_URI}.html [NC,L]
I have this folder structure:
- root
index.htmlabout.htmlexample(directory)index.htmlx.html
It works fine for:
- Main page is
x.com x.com/aboutreturns the file content of/about.htmlx.com/about.htmlredirects tox.com/aboutx.com/example/xreturns the file content of/example/x.htmlx.com/example/returns the file content of/example/index.html
But this happens also and I want to disallow this:
x.com/exampleredirects tox.com/example/x.com/exampleshould return the file content of/example/index.html
Do you have an idea what I have to change in my .htaccess file?
Since
/exampleis a physical directory, mod_dir "fixes" the URL and appends the trailing slash with a 301 (permanent) redirect. The trailing slash is required in order to server theDirectoryIndex(ie.index.htmlin this case) from that directory.However, we can prevent mod_dir from appending the trailing slash with the
DirectorySlash Offdirective. BUT, we must then issue an internal rewrite to append the trailing slash otherwise theDirectoryIndexdocument will not be served (as mentioned above). (Or, we could rewrite directly to the index document.)When setting
DirectorySlash Offwe must also ensure that directory-listings (mod_autoindex) are disabled, since the presence of an index document in that directory will no longer prevent the directory listing.To resolve potential canonicalisation issues you now need to "redirect" in the other direction to remove any trailing slash on the requested URL. eg. requests to
/example/now need to be redirected back to/example.In addition, the following rule that rewrites to the corresponding
.htmlfile is not strictly correct:The condition (that uses
REQUEST_FILENAME) is not necessarily testing the same URL that you are ultimately rewriting to in the substitution (that usesREQUEST_URI). So, in certain scenarios (eg. when requesting a non-existent file in a directory that also happens to map to a.htmlfile) you can get a rewrite loop (500 internal server error). See the following question/answer on ServerFault that goes into more detail on this: https://serverfault.com/questions/989333/using-apache-rewrite-rules-in-htaccess-to-remove-html-causing-a-500-errorThe same applies to the earlier rule that appends the
.phpextension.Bringing the above points together, we get the following:
The new rules related to your immediate issue are indicated with
[NEW].You will need to clear your browser cache before testing since the 301 (permanent) redirect that appended the trailing slash will have been cached. Test with 302 (temporary) redirects to avoid potential caching issues.
Additional notes:
There's no point testing that the request does not map to a file before testing that the same request plus
.phpdoes map to a file, these are mutually exclusive events, particularly since yourRewriteRulepattern excludes dots (so excludes URLs with file extensions), so I have removed that condition.No need to backslash-escape literal dots in the
RewriteCondTestString since this is an "ordinary" string, not a regex.No need for the
NCflag on two of theRewriteRuledirectives since the regex already matches a-z and A-Z.I grouped the canonical redirects before the internal rewrites.
Note that I also modified the regex in the condition of this rule to avoid matching a potential query string, otherwise a request of the form
/foo.html?bar.htmlwould result in a double redirect and the query string would be corrupted.NB: You don't currently have a corresponding rule for
.phprequests. (You could handle both in the same rule.)