1 subdomain doesn't support HSTS

Viewed 2222

My website is hosted on AWS and I am using SEMRush to track any server and programming issues.

And my SEMRush found this error when I run it.

2 subdomains don't support HSTS.

enter image description here

Hence I put below code to solve this issue

<IfModule mod_headers.c>
        <If "%{REQUEST_SCHEME} == 'https' || %{HTTP:X-Forwarded-Proto} == 'https'">
            Header set Strict-Transport-Security "max-age=31536000"
        </If>
    </IfModule>

Now 1 error resolved but still having one more subdomain having the same error.

enter image description here

As you can see rosterelf.com still doesn't support HSTS.

Further, I am redireting non www to www in my htaccess and this is how my .htaccess file looks like.

<IfModule mod_rewrite.c>

    <IfModule mod_negotiation.c>
        Options -MultiViews
    </IfModule>

    <IfModule mod_headers.c>
        <If "%{REQUEST_SCHEME} == 'https' || %{HTTP:X-Forwarded-Proto} == 'https'">
            Header set Strict-Transport-Security "max-age=31536000"
        </If>
    </IfModule>

    RewriteEngine On

    ##
    ## You may need to uncomment the following line for some hosting environments,
    ## if you have installed to a subdirectory, enter the name here also.
    ##
    # RewriteBase /

    ##
    ## Uncomment following lines to force HTTPS.
    ##
    # RewriteCond %{HTTPS} off
    # RewriteRule (.*) https://%{SERVER_NAME}/$1 [L,R=301]
    
    # CONDITIONS FOR ONLY LIVE SITE STARTS 
    RewriteCond %{HTTPS} off
    RewriteCond %{HTTP_HOST} !^www\. [NC]
    RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L,NE]
    # CONDITIONS FOR ONLY LIVE SITE ENDS  
    
    ##
    ## Allow robots.txt
    ##
    RewriteRule ^robots.txt - [L]

    ## 301 redirect for old support details page url to new one 
    ## OLD URL https://www.rosterelf.com/support-detail/1903/how-can-employees-clock-inout-of-time-clock-different-slug 
    ## NEW URL https://www.rosterelf.com/support-detail/how-can-employees-clock-inout-of-time-clock-different-slug 
    
    RewriteRule ^(support-detail)/\d+/([\w-]+)/?$  /$1/$2 [R=301,NC,L]
    RewriteRule ^blog-detail/\d+/([\w-]+)/?$ /blog/$1 [R=301,NC,L]



    ##
    ## Black listed folders
    ##
    RewriteRule ^bootstrap/.* index.php [L,NC]
    RewriteRule ^config/.* index.php [L,NC]
    RewriteRule ^vendor/.* index.php [L,NC]
    RewriteRule ^storage/cms/.* index.php [L,NC]
    RewriteRule ^storage/logs/.* index.php [L,NC]
    RewriteRule ^storage/framework/.* index.php [L,NC]
    RewriteRule ^storage/temp/protected/.* index.php [L,NC]
    RewriteRule ^storage/app/uploads/protected/.* index.php [L,NC]

    ##
    ## White listed folders
    ##
    RewriteCond %{REQUEST_FILENAME} -f
    RewriteCond %{REQUEST_FILENAME} !/.well-known/*
    RewriteCond %{REQUEST_FILENAME} !/storage/app/uploads/public/.*
    RewriteCond %{REQUEST_FILENAME} !/storage/app/media/.*
    RewriteCond %{REQUEST_FILENAME} !/storage/app/resized/.*
    RewriteCond %{REQUEST_FILENAME} !/storage/temp/public/.*
    RewriteCond %{REQUEST_FILENAME} !/themes/.*/(assets|resources)/.*
    RewriteCond %{REQUEST_FILENAME} !/plugins/.*/(assets|resources)/.*
    RewriteCond %{REQUEST_FILENAME} !/modules/.*/(assets|resources)/.*
    RewriteRule !^index.php index.php [L,NC]

    ##
    ## Block all PHP files, except index
    ##
    RewriteCond %{REQUEST_FILENAME} -f
    RewriteCond %{REQUEST_FILENAME} \.php$
    RewriteRule !^index.php index.php [L,NC]

    ##
    ## Standard routes
    ##
    RewriteCond %{REQUEST_FILENAME} !-f
    RewriteRule ^ index.php [L]

</IfModule>

Do I need to put below code

<IfModule mod_headers.c>
        <If "%{REQUEST_SCHEME} == 'https' || %{HTTP:X-Forwarded-Proto} == 'https'">
            Header set Strict-Transport-Security "max-age=31536000"
        </If>
    </IfModule>

Out side of <IfModule mod_rewrite.c> condition ? What should I do to solve this ?

Can someone please guide me ..

Thanks

2 Answers

Pretty sure you need always if you want a header to also be added to redirects so try this:

Header always set Strict-Transport-Security "max-age=31536000"

my SEMRush found this error when I run it.

Just to clarify, that's not strictly an "error", unless you are explicitly trying to implement HSTS. (It doesn't look as if you were?) HSTS is a recommendation.

but still having one more subdomain having the same error.

SEMRush's identification is curious; that's not a subdomain, that's the "domain apex" (ie. no subdomain).

RewriteCond %{HTTPS} off
RewriteCond %{HTTP_HOST} !^www\. [NC]
RewriteRule ^ https://www.%{HTTP_HOST}%{REQUEST_URI} [R=301,L,NE]

When implementing HSTS you need to first redirect from HTTP to HTTPS on the "same hostname", before you canonicalise the hostname, ie. before you redirect from no-www (the domain apex) to www.

This means you can't do what you are doing here... redirecting from HTTP and no-www to HTTPS and www in a single redirect. (The currently commented out HTTP to HTTPS redirect is what you need to be using instead.)

However, your current rule is also incomplete as it fails to canonicalise the hostname when requesting HTTPS. ie. It does not redirect https://example.com/ to https://www.example.com/. The first condition that checks that HTTPS is off ensures the rule is only processed for HTTP requests.

When implementing HSTS your redirects need to be written like this:

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

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

The second redirect to canonicalise the hostname does assume that you have no other subdomains that should be accessible since it redirects anything that is "not www".

Yes, this does (unfortunately) mean that users will experience two redirects when requesting http://example.com/ the very first time. But this is the nature of HSTS. And it is only the very first time that user visits HTTP on the non-canonical hostname.

<If "%{REQUEST_SCHEME} == 'https' || %{HTTP:X-Forwarded-Proto} == 'https'">
    Header set Strict-Transport-Security "max-age=31536000"
</If>

This sets the STS header on HTTPS "success" responses. However, it is also a requirement of HSTS that you set this header on the HTTPS canonical redirect from no-www to www. Currently, the above directive is only setting the header on onsuccess (200 Ok) responses (the default). To set the header on the 301 redirect you need the always condition, as @BarryPollard has already pointed out in his answer:

# Set the STS header on redirects as well as 200 OK responses.
Header always set Strict-Transport-Security "max-age=31536000"

Just to add, not only will the always condition set the header on 3xx responses, but also other 4xx responses as well etc.

The <If> expression itself is also a bit dubious. You should only check the X-Forwarded-Proto header if your application server is behind a front-end proxy that manages the secure connection. However, if you are behind a proxy then you don't need to check the REQUEST_SCHEME server variable. (It looks like this expression is intended to be a generic "catch-all".) The issue with the above expression is that if you are not behind a proxy then a malicious request could inject the X-Forwarded-Proto HTTP request header and fool your server into sending the header.

From your HTTP to HTTPS redirect (that checks the HTTPS server variable) it looks like you are not behind a front-end proxy. Although, maybe you are and we'll need to modify the above HTTP to HTTPS redirect?

So, if you are not behind a front-end proxy then the following <If> expression is preferable:

<If "%{REQUEST_SCHEME} == 'https'>

Or,

<If "%{HTTPS} == 'on'>

And if using Apache 2.2 (or Apache 2.4) you could do the following instead of using an <If> expression:

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

# Set STS header only when the HSTS env var is set (including redirects)
Header always set Strict-Transport-Security "max-age=31536000" env=HSTS
Out side of `<IfModule mod_rewrite.c>` condition ?

Placing the directive inside or outside the <IfModule> containers makes no difference in this instance (since mod_rewrite and mod_headers are presumably installed). In fact, strictly speaking, the <IfModule> wrappers should probably be removed altogether.

See my answer to the following question on the Webmasters stack for more info on this:

Related