HSTS does not cover subdomains
What is this issue?
This is advice, not a defect. The page sends an HSTS policy, but without the
includeSubDomains directive, so the policy protects this exact host name only.
Strict-Transport-Security: max-age=31536000 ← this host only
Strict-Transport-Security: max-age=31536000; includeSubDomains ← this host and every subdomainFor a page to pass this check:
- Its HSTS header includes
includeSubDomains.
Why it is a suggestion. includeSubDomains is right for most sites, but it
is a promise about every subdomain. If legacy.example.com or an internal tool
is still served over plain HTTP, adding the directive makes it unreachable in
browsers that have seen the policy. Only you know whether that is safe, so this
check points it out and deducts nothing.
When this check stays silent
- Plain HTTP pages. Browsers ignore HSTS sent over HTTP (RFC 6797 §8.1), and the page is already reported as not served over HTTPS.
- No usable policy. A missing header, a header with no
max-age, andmax-age=0are all reported by "No Strict-Transport-Security header" (#41). This check only looks at policies that browsers would actually apply, so the two never describe the same header.
Why it matters
Cookies leak across subdomains. A cookie set for
.example.comis sent to every subdomain. Ifshop.example.comis reachable over HTTP, an attacker can use it to read or plant cookies that the main site trusts.Attackers can invent subdomains. Without
includeSubDomains, an attacker on the network can send a victim tohttp://login.example.com— a name that never existed — and serve whatever they like there, under your domain.It is required for preloading. The browser preload list only accepts policies with
includeSubDomains.
Effect on the health score
None. This is a suggestion and never deducts.
How to fix it
List your subdomains — DNS records, certificates issued for your domain (search it on crt.sh), and anything internal.
Make sure each one serves HTTPS and redirects HTTP to HTTPS.
Add the directive:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"Send it from the bare domain too. The policy for
example.comis what covers*.example.com, so the apex must send the header, not justwww.
When to leave it alone
If a subdomain genuinely cannot move to HTTPS yet, leave the directive off and come back to it once it can.
Examples
Example 1: Host-only policy
Strict-Transport-Security: max-age=31536000Suggested: subdomains are not covered.
Example 2: Full coverage
Strict-Transport-Security: max-age=31536000; includeSubDomainsPasses.
Example 3: Different casing
Strict-Transport-Security: max-age=31536000; INCLUDESUBDOMAINSPasses: directive names ignore case.
Example 4: The directive only in a second header
Strict-Transport-Security: max-age=31536000
Strict-Transport-Security: max-age=31536000; includeSubDomainsSuggested: browsers use only the first header field.
How PixyScan detects this
Only HTTPS pages with a usable HSTS policy are checked. No header, no
max-ageandmax-age=0belong to "No Strict-Transport-Security header".Looks for the
includeSubDomainsdirective in the first header field, ignoring case. A token that merely contains the name, such asincludeSubDomainsLater, does not count.Raises the suggestion when it is absent.
How the header is read
PixyScan reads the header the way RFC 6797 tells a browser to:
- Only the first header field counts. If the server sends the header twice, browsers use the first one and ignore the rest, and so does this check.
- Directive names ignore case.
includeSubDomains,INCLUDESUBDOMAINSandincludesubdomainsare the same directive. - Values may be quoted.
max-age="31536000"is valid. - A repeated directive makes the header invalid. Browsers ignore a header
like
max-age=600; max-age=700entirely, so this check says nothing about it rather than giving advice about a policy no browser applies.
What we store
Storage Level
Page Level
Database Table / Prisma Model
PageSecurityHeader, and the finding on audit_issues.details
Fields Used
| Field | Type | Description |
|---|---|---|
| page_security_headers.hsts_header | String | The Strict-Transport-Security header exactly as the page sent it |
Stored Fields on the finding
| Field | Type | Description |
|---|---|---|
| message | String | What is missing from the policy and why it matters |
| hstsHeader | String | The header as sent |
Detection Dependencies
- HTTP Response headers
- HTTPS (the check only applies to pages served over HTTPS)