Why does my .htaccess file only partially force HTTPS and WWW?
.htaccess files are extremely useful in many cases for users who either do not have root permissions or for users who simply aren't comfortable in making changes in their web server's configuration file. Trying to debug .htaccess not working isn't always the easiest thing to do, however, hopefully by checking the discuss below mentioned about htaccess, 301-redirect, https, , .htaccess common problems as well as the troubleshooting tips, you'll have a better grasp on what you may have to modify to get your .htaccess file running smoothly.Problem :I have the domain https://www.example.com and this is how I prefer that users reach my site as well, using both HTTPS and WWW.
In my .htaccess file I try and force everyone to use this set up with the following code
RewriteEngine On
RewriteCond %HTTP_HOST ^example.com [NC]
RewriteCond %SERVER_PORT 80
RewriteRule ^(.*)$ https://www.example.com/$1 [R=301,L]
Which seems to work fine, all users that type example.com will get directed to the HTTPS version with the www sub domain.
BUT! If a user were to enter www.example.com OR http://www.example.com they are not redirected to the HTTPS version, what can be causing this?
I'm working on preparing the SEO for that domain as well and SEMRush actually gives me two listings per finding, one for the http version of the site and one for the https version of the site, I'm guessing this has something to do with the redirection issues as well.
UPDATE
This is my entire .htaccess file as it looks after your last suggested changes:
RewriteEngine On
RewriteCond %HTTPS !on [OR]
RewriteCond %HTTP_HOST =goodies.no [NC]
RewriteRule (.*) https://www.goodies.no/$1 [R=301,L]
#RewriteCond %HTTP_HOST ^goodies.no [NC]
#RewriteCond %SERVER_PORT 80
#RewriteRule ^(.*)$ https://www.goodies.no/$1 [R=301,L]
# compress text, html, javascript, css, xml:
AddOutputFilterByType DEFLATE text/plain
AddOutputFilterByType DEFLATE text/html
AddOutputFilterByType DEFLATE text/xml
AddOutputFilterByType DEFLATE text/css
AddOutputFilterByType DEFLATE application/xml
AddOutputFilterByType DEFLATE application/xhtml+xml
AddOutputFilterByType DEFLATE application/rss+xml
AddOutputFilterByType DEFLATE application/javascript
AddOutputFilterByType DEFLATE application/x-javascript
# Or, compress certain file types by extension:
<files *.svg>
SetOutputFilter DEFLATE
</files>
<files *.js>
SetOutputFilter DEFLATE
</files>
<files *.ttf>
SetOutputFilter DEFLATE
</files>
<files *.jpg>
SetOutputFilter DEFLATE
</files>
<files *.png>
SetOutputFilter DEFLATE
</files>
## EXPIRES CACHING ##
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType image/jpg "access 1 year"
ExpiresByType image/jpeg "access 1 year"
ExpiresByType image/gif "access 1 year"
ExpiresByType image/png "access 1 year"
ExpiresByType text/css "access 1 month"
ExpiresByType text/html "access 1 month"
ExpiresByType application/pdf "access 1 month"
ExpiresByType text/x-javascript "access 1 month"
ExpiresByType application/x-shockwave-flash "access 1 month"
ExpiresByType image/x-icon "access 1 year"
ExpiresDefault "access 1 month"
</IfModule>
## EXPIRES CACHING ##
I just commented out the part that used to work, and as you can see there are some compression and caching rules there as well.
UPDATE#2 (ACTUALLY UPDATE 3, NEW TEST.PHP FILE)
here is the contents of test.php file which is now available at https://goodies.no/test.php
<?php
$ip = getenv('SERVER_PORT');
echo "the first variable (SERVER_PORT) is ".$ip;
$ap = getenv('HTTPS');
echo " the second variable (HTTPS) is ".$ap;
$dp = getenv('ENV_HTTPS');
echo " the third (ENV_HTTPS) variable is "$.dp
?>
This only outputs the text "the variable is" and nothing else. Am I way off.... I get the feeling that I'm way off .. :/
You need to change your logic. Your conditions are implicitly AND'd (the default), they should be OR'd instead. So, currently the RewriteRule is only processed if the HTTP_HOST is "example.com" AND the SERVER_PORT is 80. You need it to trigger on either of these conditions, so you need to add an OR flag on the first condition.
For example:
RewriteCond %HTTP_HOST ^example.com$ [NC,OR]
RewriteCond %SERVER_PORT 80
RewriteRule (.*) https://www.example.com/$1 [R=301,L]
(No need for string anchors ie. ^ and $ if you are capturing the whole pattern.)
UPDATE: Alternatively, check the HTTPS server variable, instead of SERVER_PORT. For example:
RewriteCond %HTTPS !on [OR]
RewriteCond %HTTP_HOST =example.com [NC]
RewriteRule (.*) https://www.example.com/$1 [R=301,L]
What that says is... for every request that is not HTTPS or is for example.com (ie. no www) then redirect to https://www.example.com.
Alternatively, you can implement this as two separate rules:
RewriteCond %HTTPS !on
RewriteRule (.*) https://www.example.com/$1 [R=301,L]
RewriteCond %HTTP_HOST =example.com [NC]
RewriteRule (.*) https://www.example.com/$1 [R=301,L]
DEBUGGING: Test what these server variables contain on your server. You can (temporarily) replace the above redirect with the following:
RewriteCond %SERVER_PORT (.*)
RewriteRule ^ - [E=ENV_SERVER_PORT:%1]
RewriteCond %HTTPS (.*)
RewriteRule ^ - [E=ENV_HTTPS:%1]
This will create two environment variables (ENV_SERVER_PORT and ENV_HTTPS) that should be available to your server-side script (eg. PHP). Check what these contain. (eg. in PHP echo getenv('ENV_SERVER_PORT');)
Under HTTP you would expect... ENV_SERVER_PORT to be 80 and ENV_HTTPS to be "off".
Under HTTPS you would expect... ENV_SERVER_PORT to be 443 (default) and ENV_HTTPS to be "on".
Solution
It would seem that the SERVER_PORT and HTTPS Apache server variables are not available. This suggests that you have a front end proxy (or load balancer) that is handling the request.
The related (but not exactly the same - despite having the same name) PHP superglobals SERVER_PORT and HTTPS (specifically $_SERVER['SERVER_PORT'] and $_SERVER['HTTPS']) are, however, being set (by PHP). But SERVER_PORT is always 80 when served over HTTP and HTTPS (this also suggests a front-end proxy). The HTTPS PHP superglobal is, however, correctly set to "on" when served over HTTPS. But we want to avoid doing this redirection in our code (less efficient, more chance of error).
From your server output, it looks like there is an X-Proto HTTP request header (identified by the HTTP_X_PROTO index in the $_SERVER superglobal array) being set (most probably by the proxy server) when served over HTTPS (it is set to the string "SSL"), but when served over a plain HTTP connection this header is not set. We should be able to check for this in .htaccess. Try the following:
RewriteCond %HTTP:X-Proto !SSL [OR]
RewriteCond %HTTP_HOST =example.com [NC]
RewriteRule (.*) https://www.example.com/$1 [R=301,L]
For all requests that do not have an X-Proto HTTP request header set to the string "SSL" or the apex domain is requested then redirect to HTTPS with the www subdomain.
Comments
Post a Comment