1

I wanted to submit my website on my own Debian Apache2 server to the HSTS preload list.

It told me I have several things incorrectly set up, so I made adjustments, now it seems to be satisfied. There is just one thing I am unsure how it works or if I've done it correctly? The following redirection code to https and www is placed directly in a Virtual Host configuration file. Could someone have a look and explain to me how it works and if there are any errors, please suggest corrections.

RewriteEngine          on
RewriteCond            %{HTTPS} !on
RewriteRule            ^/?(.*) https://%{SERVER_NAME}/$1 [R=301]
RewriteCond            %{HTTP_HOST} ^www\.(.*)$ [NC]
RewriteRule            ^(.*)$ https://%1/$1 [R]

EDIT1:

All lines of this code are placed in HTTP (port 80) section.

EDIT2:

I am unsure whether to strip the www or add it yet. So I would welcome to see both approaches if it's not so hard to implement.

2

placed directly in a Virtual Host

But where exactly? If you've implemented SSL then you'll have at least two VirtualHost containers, one for SSL/HTTPS (port 443) and the other for HTTP (port 80).

You will need to have placed these directives in the VirtualHost container for port 80 in order to have successfully implemented the HTTP to HTTPS redirect. However, if this is the case, then the check against HTTPS is superfluous. But also, if these directives are only in the VirtualHost for port 80 then the www to non-www canonicalisation will never occur when accessing the non-canonical https://www.example.com URL directly. (Or have you put these directives in both VirtualHosts? Which is unnecessary.)

RewriteRule            ^/?(.*) https://%{SERVER_NAME}/$1 [R=301]

Without the L flag, processing will continue. If you are requesting http://www.example.com then it will redirect to https://example.com. Ordinarily this is OK, however, this would seem to break one of the prerequisites of the HSTS preload list which states you should redirect to the same host. (So, it's hard to say how this appears to be "working" with regards to satisfying the criteria for the HSTS preload list?)

%{SERVER_NAME} - whilst this may work OK on your server, this is not the most reliable variable to use in this instance, since you want to always redirect to the same host (not the defined ServerName). You should use HTTP_HOST instead here to ensure you redirect to the same host as specified on the request. (On a default server config, SERVER_NAME is the same as HTTP_HOST, however, this is dependent on whether UseCanoncialName has been set.)

RewriteRule            ^(.*)$ https://%1/$1 [R]

As @ltai already mentioned in his answer, this is a temporary (302) redirect and should be a permanent (301) redirect. You are also missing the L flag, which could cause problems if you add later directives.

Also, when used in a VirtualHost context, this will result in a double slash in the RewriteRule substitution, eg. https://example.com//<anything> since the captured pattern includes the slash prefix (in a VirtualHost context). In your first RewriteRule, you specifically exclude the slash prefix (ie. ^/?(.*)); you should be doing the same here.


These directives should be split across the two (or more) VirtualHosts. For example:

<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com

# Unconditional redirect to HTTPS on the same host
# (The "same host" is a requirement of the HSTS preload list)
RewriteEngine On
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]
</VirtualHost>

<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com

# SERVER_NAME now refers to the value of ServerName, not HTTP_HOST
UseCanonicalName On

# Redirect from www to the canonical ServerName
RewriteEngine On
RewriteCond %{HTTP_HOST} ^www\. [NC]
RewriteRule ^ https://%{SERVER_NAME}%{REQUEST_URI} [R=301,L]
</VirtualHost>

Ordinarily, you would achieve the HTTP to HTTPS and www to non-www canonicalisation in a single redirect. However, a requirement of the HSTS preload list states you should redirect HTTP to HTTPS on the same host, which means you'll need to keep it as two. (However, once you're on the HSTS preload list then the HTTP to HTTPS redirect will never occur anyway.)

1

The only thing I would change is also set the last rule to give a permanent redirect.

RewriteEngine          on

That line says to turn on the redirect engine, without which any rule would not work.

RewriteCond            %{HTTPS} !on

That says that the condition is that the scheme is not https. This is the same as if you used off as opposed to !on which is not on.

RewriteRule            ^/?(.*) https://%{SERVER_NAME}/$1 [R=301]

This is the rule to execute when HTTPS is NOT on. It says take whatever is after the starting slash (if there is one) or the whole part of the URL and redirect to HTTPS with the same server. The response here is a 301 which is a permanent redirect.

RewriteCond            %{HTTP_HOST} ^www\.(.*)$ [NC]

The line above says the condition is that the hosts starts with www. and that anything after that is captured to use in the next rule. That is why it has a (.*)

RewriteRule            ^(.*)$ https://%1/$1 [R]

This rule says to redirect any request to https:// followed by the part of the host that was after the www (%1) and then followed by the request ($1). The result is a 302 TemporaryRedirect since you did not specify a return code such as R=301 as you did 2 lines higher.

The nice thing about this code is that you have not hardcoded the server or domain name, so it is quite generic and may be used on a number of sites as is.

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.