The code above works as expected. Why isn't the code in the next format, as it does the same thing? Is any difference ?
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^(.*)$ https://www.%{HTTP_HOST}/$1 [R=301,L]
This code is different to your "working" code (at the top of your question). In this code, the www
subdomain is always prefixed to the requested hostname. So, if you were to request http://www.example.com/foo
(HTTP + www) then it will redirect to https://www.www.example.com/foo
- which is presumably not the intention.
This can be "fixed" with an additional condition in order to capture the hostname less the "optional" www
subdomain, however, this is still not "the same" as the "working" directives you posted. For example:
RewriteCond %{HTTPS} !=on [OR]
RewriteCond %{HTTP_HOST} !^www\.
RewriteCond %{HTTP_HOST} ^(?:www\.)?(.+?)\.?$ [NC]
RewriteRule ^(.*)$ https://www.%1/$1 [R=301,L]
The only purpose of the 3rd condition is to capture the hostname, less the www.
prefix (if any). This condition is always successful. The hostname (less the www.
prefix) is stored in the %1
backreference.
Just to emphasise... this is not exactly the same as the "working" redirects you posted initially. This performs the HTTPS + www redirect in 1 go, instead of two (potentially) in your "working" directives. eg. When you request HTTP and non-www then your "working" redirects will trigger 2 redirects, first to HTTPS on the same host*1 and then to HTTPS + www.
*1 This is a necessary requirement if you have implemented HTTP Strict Transport Security (HSTS) - where you must redirect to HTTPS on the same host before canonicalising the hostname. So, one redirect is not always "better" than two.
The first line of code looks a little bit ambiguous. So I try to modify with:
RewriteCond %{HTTPS} != on # Put a space before on
It may look ambiguous to you, but I assure you it's not.
You cannot simply inject spaces here, since spaces are argument delimiters in Apache config files. So, by putting a space after the !=
you are now passing 3 arguments to the RewriteCond
directive: %{HTTPS}
, !=
and on
. on
is likely to trigger an "invalid flags" error, since this will be interpreted as the 3rd "flags" parameter (eg. normally something like [NC]
).
The =
and !
are prefix-operators and are in fact part of the argument (CondPattern). They change the way the CondPattern (2nd argument) is interpreted. !=
is not a single operator, it does not strictly mean not-equals, as you are perhaps expecting, although the net result is the same. (More on this below).
The only time you need to use double-quotes (not single-quotes) around an argument in Apache config files is if the argument contains spaces (since, as mentioned above, spaces are argument delimiters). Although you can always surround the argument in double-quotes if you wish. And you can sometimes backslash escape the space instead (if the argument is a regex).
But note, the double-quotes surround the entire argument (which includes the prefix operators). For example:
RewriteCond %{HTTPS} "!=on"
Aside: Apache does not support line-end comments, so the # Put a space before on
at the end of the line is not valid. Due to the way Apache directives are processed, this may appear to be OK at times (ie. no error), but it is strictly invalid. (You may have only used this in the code sample in your question, however, it is a common source of error, so worth mentioning.)
- I found some users which are using:
RewriteCond %{HTTPS} off
and not:
RewriteCond %{HTTPS} !=on
I understand that one line checks if the https is off and another one if https is not on. We can say they do the same thing. There are other differences here ?
I've actually answered the same question on the Webmasters stack previously, so I'll just link to that for more detail:
But yes, the net result is indeed the same and it's primarily personal preference which method you choose. Although the two methods are technically different.
off
is a regex comparison: Does off
occur anywhere in the value of the HTTPS
server variable?
!=on
is not a regex comparison. The =
prefix changes it to a lexicographical string comparison. ie. Is the value of the HTTPS server var exactly equal to "on"? The !
prefix then negates the expression. So the condition is successful when it does not match.
Can HTTPS
not be neither on or off ?
The HTTPS
server variable can only be "on" or "off" if your server is correctly configured.
If the answer is yes, that means the line RewriteCond %{HTTPS} !=on
is better than RewriteCond %{HTTPS} off
. Isn't it ?
Not necessarily. If HTTPS
is not "on" or "off" then it's more likely to not be set at all (on all requests), so !=on
would always be successful and result in a redirect loop. Whereas off
will simply fail. Although this is hypothetical, your server would have to be "broken" for the HTTPS
server variable not to be set at all and to not be "on" or "off".
- When forcing www I have
E=HTTPS:1
flag, which means I set the variable HTTPS with value 1.
First, a bit of clarification... there are 2 variables here:
- The
HTTPS
server variable as set by Apache and referenced in %{HTTPS}
. This is effectively readonly.
- The
HTTPS
environment variable that you are setting here. This would be readable using the syntax %{ENV:HTTPS}
.
Whilst it is relatively common to see this, it is arguably confusing to have two different variables of the same name. It would be better to change your environment variable to something like HSTS
instead (see below), to avoid any confusion.
I don't understand the reason... When and who checks for this variable ?
Well, in the directives you posted it's not required. So, maybe this is not required here? But...
The reason for setting an env var on the HTTPS redirect like this is a common pattern when implementing HSTS*2. When implementing HSTS you need to set the Strict-Transport-Security
HTTP response header on the HTTPS to HTTPS redirect itself. This is why I suggested using a variable called HSTS
instead. If you are not setting this header with a conditional Header
directive elsewhere in your config file (based on this env var) then you are not implementing HSTS (properly) and this env var is most probably superfluous.
*2 Although I wouldn't necessarily do it this way myself. I would set the env var in a separate rule, since you still need to set the STS header on other HTTPS responses, not just the redirect. See my answer to the following question on the Webmasters stack for more information on this, with example code:
Here, on question 4 I try to rename HTTPS to HTTPSaaa and then, with php, to get all server variable (from $_SERVER). I don't find HTTPSaaa. So this means that the flag is used only by htaccess ? So, I ask again. What's the reason for this flag/variable to exist ?
Yes, it's only used by Apache/.htaccess
. It is set only on the redirect response back to the client. Your PHP script is not processed in this time, so cannot read it.
The reason would be to help with implementing HSTS, as described above.
If you are interested in HSTS then see this related CodeReview question (and my answer) regarding the implementation of HSTS in .htaccess
: