Reputation: 27
I have copied the htaccess file from my other sites (that all work, and are on the same server) and have only changed URLs that need changing (ie when redirecting from non-www to www), but it simply does not work.
My changing of, for example, /index.php
to /index
fails, but /example.html
does work at /example
, which makes me think it's an issue with PHP or htaccess acting with PHP?
I've asked several friends of mine to have a look at it and they cannot understand why it's happening.
My .htaccess file is:
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www.example.com [NC]
RewriteRule ^(.*)$ https://example.com/$1 [L,R=301]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.php -f
RewriteRule ^(.*)$ $1.php [NC,L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME}\.html -f
RewriteRule ^(.*)$ $1.html [NC,L]
And my VirtualHost is:
<VirtualHost *:443>
ServerName example.com
ServerAlias localhost
DocumentRoot "/var/www/example.com/public_html"
<Directory "/var/www/example.com/public_html">
Options +Includes +FollowSymLinks +MultiViews
AllowOverride All
Require all granted
</Directory>
SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
<VirtualHost *:80>
ServerName example.com
ServerAlias localhost
DocumentRoot "/var/www/example.com/public_html"
<Directory "/var/www/example.com/public_html">
Options +Includes +FollowSymLinks +MultiViews
AllowOverride All
Require all granted
</Directory>
RewriteEngine on
RewriteCond %{SERVER_NAME} =example.com [OR]
RewriteCond %{SERVER_NAME} =localhost
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
<VirtualHost *:443>
ServerName www.example.com
ServerAlias localhost
DocumentRoot "/var/www/example.com/public_html"
<Directory "/var/www/example.com/public_html">
Options +Indexes +Includes +FollowSymLinks +MultiViews
AllowOverride All
Require all granted
</Directory>
SSLCertificateFile /etc/letsencrypt/live/www.example.com/fullchain.pem
SSLCertificateKeyFile /etc/letsencrypt/live/www.example.com/privkey.pem
Include /etc/letsencrypt/options-ssl-apache.conf
</VirtualHost>
<VirtualHost *:80>
ServerName www.example.com
ServerAlias localhost
DocumentRoot "/var/www/example.com/public_html"
<Directory "/var/www/example.com/public_html">
Options +Indexes +Includes +FollowSymLinks +MultiViews
AllowOverride All
Require all granted
</Directory>
RewriteEngine on
RewriteCond %{SERVER_NAME} =www.example.com [OR]
RewriteCond %{SERVER_NAME} =localhost
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [END,NE,R=permanent]
</VirtualHost>
(I've replaced the domain with example.com -- they're not like that in htaccess or the vhost.)
.htaccess is certainly not disabled, if I make a redirect it works just fine, for example:
Redirect 301 /test https://example.com/test
and...
root@server:~# a2enmod rewrite
Module rewrite already enabled
I've never experience any similar issues with my web-server before, and I'm really puzzled as to why the issue is occurring.
Upvotes: 1
Views: 950
Reputation: 45958
You don't actually state what is happening (errors? response codes? etc.), however, there are a few issues with your config with respect to extensionless URLs.
Options +Indexes +Includes +FollowSymLinks +MultiViews
You are explicitly enabling MultiViews
in your server config, however, this is going to conflict with the mod_rewrite directives in .htaccess
that attempt to append the file extension. MultiViews
needs to be disabled. eg. -MultiViews
(not +
).
However, you are also enabling directory indexes (Indexes
) and server-side-includes (Includes
) for some reason? Is this intentional? And, rather confusingly, you are setting different options for your www subdomain than for the domain apex?
In the vHost container it is often preferable to state just the options needed. For example:
Options FollowSymLinks
(This naturally disables MultiViews by not setting it.)
RewriteCond %{REQUEST_FILENAME} !-d RewriteCond %{REQUEST_FILENAME}\.php -f RewriteRule ^(.*)$ $1.php [NC,L]
These mod_rewrite directives in .htaccess
to append the file extension are not strictly correct. Whilst they may work for your "valid" URLs, a malicious user could construct a URL of the form /index/<anything>
that would otherwise map to /index.php
, to trigger a rewrite-loop (500 Internal Server Error)*1.
These should be written like this instead (assuming your .htaccess
file is in the document root) to avoid this vulnerability:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}/$1.php -f
RewriteRule (.*) $1.php [L]
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}/$1.html -f
RewriteRule (.*) $1.html [L]
The literal dot does not need to be escaped in the TestString (first argument to the RewriteCond
directive) since this is a "string", not a regex. The NC
flag is not required here.
If your URLs themselves don't contain "file extensions" then you can optimise the above by not testing URLs that already look like they have a file extension. For example:
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{DOCUMENT_ROOT}%{REQUEST_URI}.php -f
RewriteRule !\.\w{2,4}$ %{REQUEST_URI}.php [L]
The negated regex !\.\w{2,4}$
only matches URLs that don't contain - what looks like - file extensions. A file extension in this context is a sequence of 2 to 4 letters/digits at the end of the URL-path, preceded by a dot.
*1 See my answer to the following ServerFault question that goes into more detail regarding the potential rewrite-loop and resulting 500 error response:
Upvotes: 1