2

I am trying to implement the HSTS (HTTP Strict Transport Security) on my Wordpress site, but I am not getting any success. First I had managed to redirect my site from non-www to www , and including https:// , but I got the message on https://hstspreload.org/ that it should redirect to www first.

I was trying to use the VirtualHosts config file, no luck. So I did some googling and found this link which looked like a solution with htaccess, but I am still getting the problem. If anyone knows how to implement this via the VirtualHost / Apache configuration files, that would be real great.

Error: HTTP redirects to www first http://inter.net (HTTP) should immediately redirect to https://inter.net (HTTPS) before adding the www subdomain. Right now, the first redirect is to https://www.inter.net/. The extra redirect is required to ensure that any browser which supports HSTS will record the HSTS entry for the top level domain, not just the subdomain.

My htaccess is below:

# BEGIN WordPress
# The directives (lines) between "BEGIN WordPress" and "END WordPress" are
# dynamically generated, and should only be modified via WordPress filters.
# Any changes to the directives between these markers will be overwritten.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^index\.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /index.php [L]
</IfModule>

# END WordPress

#### This is what I added : From https://www.danielmorell.com/guides/htaccess-seo/redirects/https-www-and-trailing-slash 
#### Force HTTPS://WWW and remove trailing / from files ####
## Turn on rewrite engine
RewriteEngine on

# Remove trailing slash from non-filepath urls
RewriteCond %{REQUEST_URI} /(.+)/$
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ https://www.inter.net/%1 [R=301,L]

# Include trailing slash on directory 
RewriteCond %{REQUEST_URI} !(.+)/$
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^(.+)$ https://www.inter.net/$1/ [R=301,L]

# Force HTTPS and WWW 
RewriteCond %{HTTP_HOST} !^www\.(.*)$ [OR,NC]
RewriteCond %{https} off  
RewriteRule ^(.*)$ https://www.inter.net/$1 [R=301,L]

# Yoast SEO - XML Sitemap Rewrite Fix
RewriteEngine On
RewriteBase /
RewriteRule ^sitemap_index.xml$ /index.php?sitemap=1 [L]
RewriteRule ^locations.kml$ /index.php?sitemap=wpseo_local_kml [L]
RewriteRule ^geo_sitemap.xml$ /index.php?sitemap=geo [L]
RewriteRule ^([^/]+?)-sitemap([0-9]+)?.xml$ /index.php?sitemap=$1&sitemap_n=$2 [L]
RewriteRule ^([a-z]+)?-?sitemap.xsl$ /index.php?yoast-sitemap-xsl=$1 [L]
# END Yoast SEO - XML Sitemap Rewrite Fix

ps - the inter.net url is just for example.

EDIT - I have edited my example.com.conf file to add the extra rules given my MrWhite in the answer below - which looks accurate. After running command apachectl configtest Syntaw was OK. Ran service apache2 reload for changes to take effect , and got all browsers saying that the page is not redirecting properly : **ERR_TOO_MANY_REDIRECTS** ( cleared cache each time for each different browser ).

I reverted the htaccess to the original Wordpress and Yoast SEO rules only.

My current configuration file on apache for this VirtualHost maybe has problems but there is no syntax error with apachectl configtest : https://paste.ofcode.org/vr25hFkPEt2vYjpM5sAUxK

I tried to use Firefox Developer module (F12) to see if I could understand any additional info, the problem seems to be a 301 redirect loop to https://www.example.com

EDIT 2 : Thanks to @MrWhite , I understood that the ServerAlias detail was unecessary and was the cause of the loops. Problem solved and learned from that.

MrWhite
  • 11,643
  • 4
  • 25
  • 40
mlclm
  • 169
  • 5
  • 1
    "message on `https://hstspreload.org/`" - Are you also wanting to submit to the HSTS preload list? – MrWhite Nov 29 '20 at 15:55
  • 1
    In the title of your question you state "redirection to non-www", but in the remainder of your question you are redirecting "to www"? – MrWhite Nov 29 '20 at 16:08
  • @MrWhite - Correct, and correct ( I need the final url to be *https:// www .* .HSTS requires to redirect to non-www *first* , then to *https:// www* ) – mlclm Nov 30 '20 at 07:37
  • @MrWhite - I was referring to "non-www" in the title of my question because it's supposed to be the first redirection required by HSTS , could have put both in the title non-www and www. – mlclm Nov 30 '20 at 07:59
  • Please use `example.com` rather than `inter.net` as your example host. Or use your actual domain. – TRiG Nov 30 '20 at 09:54
  • 1
    "HSTS requires to redirect to non-www first , then to https:// www" - That's not true (or at best misleading). For instance, you shouldn't redirect from www to non-www (when the canonical URL is www). The important criteria is that you first redirect from HTTP to HTTPS **on the same host** (www or non-www). As stated in my answer. – MrWhite Nov 30 '20 at 09:57

1 Answers1

3

To summarise, the main requirements of HSTS are:

  1. Redirect from HTTP to HTTPS on the same host. ie. http://example.com to https://example.com and http://www.example.com to https://www.example.com

  2. Redirect to the canonical hostname (www or non-www) on HTTPS only. (ie. after #1 above)

  3. Send the Strict-Transport-Security (STS) HTTP response header when on HTTPS only. Including on the canonical redirect (#2 above).

    (Although several sources state that the STS header should only be sent over HTTPS and is even wholly invalid to send it over plain HTTP, I don't believe this to be the case. The spec states that the UA should simply ignore this header when sent over HTTP, so it's not a "problem" to send this over HTTP as well. However, it's not much work to send this over HTTPS only, which is how I've implemented this below.)

Which therefore means you cannot necessarily canonicalize the request (HTTP / HTTPS / www / non-www) in a single redirect as this could violate #1 above.

You also don't seem to be setting the STS header in the code you have posted. If you are implementing the redirect in Apache (server config or .htaccess) then you can't set this header using WordPress - if that is what you are doing?

I did some googling and found this link which looked like a solution with htaccess

That "solution" is not implementing HSTS. The sole purpose of that article is to canonicalise the request in a single redirect. The "warning" at the top of that article explicitly tells you that it violates HSTS.

You've also put the directives in the wrong order. These "redirect" directives need to go before the WordPress front-controller, otherwise it's simply not going to get processed for WordPress's virtual URLs.

I assume your canonical hostname is www.example.com. (Although you mention a redirect to non-www in the title of your question, you are redirecting to www throughout the rest of your question?)

I was trying to use the VirtualHosts config file, no luck.

Although it is arguably simpler and less prone to error and more efficient to implement this in the server config (using separate VirtualHosts).

For example (omitting the "other" necessary directives):

<VirtualHost *:80>
    ServerName example.com
    # Redirect to HTTPS - same host
    Redirect 301 / https://example.com/
</VirtualHost>

<VirtualHost *:80>
    ServerName www.example.com
    # Redirect to HTTPS - same host
    Redirect 301 / https://www.example.com/
</VirtualHost>

<VirtualHost *:443>
    ServerName example.com
    # Redirect to canonical host (HTTPS only)
    Redirect 301 / https://www.example.com/

    # SSL directives...

    # Set STS header on the HTTPS redirect ("always" argument is required for this)
    Header always set Strict-Transport-Security "max-age=2592000; includeSubDomains"
</VirtualHost>

<VirtualHost *:443>
    # Canonical host
    ServerName www.example.com

    # SSL directives...
    # etc.    

    # Set STS header on the HTTPS response
    Header always set Strict-Transport-Security "max-age=2592000; includeSubDomains"
</VirtualHost>

Note that the STS header in the above only sets the max-age parameter for a period of 1 month and does not include the preload parameter. Be sure to follow the instructions given in the "deploy requirements" of the HSTS preload list, if that is the intention. https://hstspreload.org/#deployment-recommendations

Alternatively, to implement this in .htaccess

(NB: I've not implemented the "trailing slash" redirect, since you've not mentioned this in your requirements and it's simply part of the code copied from the external article.)

# Set HSTS env var only if HTTPS
RewriteCond %{HTTPS} on
RewriteRule ^ - [E=HSTS:1]

# Redirect HTTP to HTTPS on the same host
RewriteCond %{HTTPS} !=on
RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

# Redirect non-www to www (HTTPS only)
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L]

# Set HSTS header conditionally if request is over HTTPS only (based on HSTS env var)
Header always set Strict-Transport-Security "max-age=2592000; includeSubDomains" env=HSTS

# Yoast SEO - XML Sitemap Rewrite Fix
# : (directives go here...)
# END Yoast SEO - XML Sitemap Rewrite Fix

# BEGIN WordPress
# : (directives go here...)
# END WordPress

The always condition is required on the Header directive so the header is set on non-200 OK responses. ie. it needs to be set on the non-www to www HTTPS 301 redirect.

See also my answer to the following question on CodeReview SE regarding the implementation of HSTS in .htaccess:

MrWhite
  • 11,643
  • 4
  • 25
  • 40
  • Thanks for your explanation. After trying to edit my example.com.conf file( VirtualHost config file and many syntax error checks and fixes, the service apache2 reload stopped any error message. I though it was ok. But, the browser is not redirecting properly. I get the message "The page isn't redirecting properly" on FF, IE, Chrome (cleared cache each time). I reverted the htaccess to the original Wordpress and Yoast SEO htaccess rules. I don't know if everything is really correct in my current configuration file : https://paste.ofcode.org/vr25hFkPEt2vYjpM5sAUxK – mlclm Nov 30 '20 at 09:25
  • To be exact, Chrome gives `ERR_TOO_MANY_REDIRECTS` . – mlclm Nov 30 '20 at 09:50
  • 1
    You need to remove the `ServerAlias` directives from your vHost containers (as above), otherwise your vHosts "conflict" and you will indeed get a redirect loop. In this scenario, each vHost should only serve a single hostname. In your config each vHost is serving the same `example.com` and `www.example.com` hostnames (only the first vHost will match). – MrWhite Nov 30 '20 at 10:02
  • 1
    _Aside:_ You have quite a lot of unnecessary repetition in your config file. The vHosts for port 80 can be quite minimal (no `DocumentRoot`, no `` containers) since you are HTTPS only and _not_ using `.htaccess`. The `` container should be moved to the main server config, outside of the vHost containers. You don't appear to be allowing access for port 443 (as you are for port 80)? You are using Apache 2.2 auth directives? – MrWhite Nov 30 '20 at 10:17
  • 1
    Yes, you nailed it, the ServerAlias was causing the issue. Thanks for all your golden explanations. Bookmarking your posts, as they're extremely well detailed and allowing noobs like me to learn. Thanks for making the internet a safer and more secure place. Yes I am pretty sure I am using *some* Apache 2.2 auth directives. I built/maintained my server progressively, so always had the feeling that my configuration file was clustered with extra non essential stuff... Thanks for pointing me in the right direction. I will remove the container hopefully without breaking anything now. – mlclm Nov 30 '20 at 10:18