HSTS and Wordpress redirection to www and non-www and https - avoid multiple redirections?
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 tohttps://inter.net
(HTTPS) before adding the www subdomain. Right now, the first redirect is tohttps://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.
To summarise, the main requirements of HSTS are:
-
Redirect from HTTP to HTTPS on the same host. ie.
http://example.com
tohttps://example.com
andhttp://www.example.com
tohttps://www.example.com
-
Redirect to the canonical hostname (www or non-www) on HTTPS only. (ie. after #1 above)
-
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
:
- https://codereview.stackexchange.com/questions/250805/hsts-recommendations-in-htaccess