2

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 .. :/

  • "and nothing else. Am I way off...." - that is unusual. (It means Apache is not setting these variables). That certainly explains the redirect loop you were getting. This suggests there is a front end proxy (or load balancer) that is handling the SSL. – MrWhite Nov 24 '16 at 20:23
  • Thanks, I can read any variable that are global (like SERVER_PORT) but the environment variables cannot be read. – lundzern Nov 24 '16 at 20:27
  • The environment variables are likely to be an empty string (they should still be set). Try assigning the output to a variable and var_dump($myvar). eg. $envServerPort = getenv('ENV_SERVER_PORT'); var_dump($envServerPort); - it will be (bool)false if not set at all, or string(0) "" if just empty. – MrWhite Nov 24 '16 at 20:37
  • Try adding <pre><?php var_dump($_SERVER); ?></pre> to your test.php file. This will output a lot of information about the system, including (converted) HTTP request headers. – MrWhite Nov 24 '16 at 20:40
  • Just to add, the PHP variables (ie. SERVER_PORT and HTTPS) are not quite the same as the Apache variables of the same name. If the ENV_ variables are empty then the Apache variables are not set. If the SERVER_PORT and HTTPS variables are set then we can do the appropriate redirect in PHP, but that doesn't really help with letting Apache do it. – MrWhite Nov 24 '16 at 20:45
2

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.

  • hmm, now my .htaccess looks like this; RewriteEngine On RewriteCond %{HTTP_HOST} ^goodies\.no [NC,OR] RewriteCond %{SERVER_PORT} 80 RewriteRule (.*) https://www.goodies.no/$1 [R=301,L] But that does not work at all (try it yourself :) – lundzern Nov 24 '16 at 16:31
  • There's no reason why that should "not work at all" - if your earlier redirect partially worked. Make sure you've cleared the browser cache - earlier 301s will have been cached by any intermediary caches. – MrWhite Nov 24 '16 at 16:45
  • but have you tried opening the site? in chrome it just changes the address line to www. I am using an incognito version of chrome but also tried safari with cleared cache – lundzern Nov 24 '16 at 16:50
  • it seems as as soon as I enter the OR in the first line the site gets some issues. with only the NC statement there things work, but just not like how I want them to :) – lundzern Nov 24 '16 at 16:53
  • How is the SSL implemented? – MrWhite Nov 24 '16 at 16:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.