Skip to content
Issue docs

HSTS header not eligible for preload

Suggestionhsts_not_preload_readyIssue 172

What is this issue?

This is advice, not a defect. The page sends an HSTS policy, but the header does not meet the requirements of the browser HSTS preload list.

HSTS normally only protects a visitor after their first visit, because that is when the browser first sees the header. The preload list closes that gap: Chrome, Firefox, Safari and Edge ship with a built-in list of domains that are HTTPS-only from the very first request.

To be accepted, the header must have all three of:

  • max-age of at least 31536000 (one year)
  • includeSubDomains
  • preload
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

Why it is a suggestion. Preloading is a long-term commitment. Removal from the list takes months to reach users' browsers, and every subdomain must stay on HTTPS. It is the right end state for many sites, but it is a decision, not a fix.

A header that says preload but would be refused is called out specifically: the site believes it asked for preloading, and the list will reject the submission.

The header is only half of it. hstspreload.org also requires that the bare domain serves the header and redirects HTTP to HTTPS on the same host. This check looks at the header on each page; check the domain itself at hstspreload.org before submitting.

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, and max-age=0 are 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

  • It protects the very first visit. Without preloading, a first-time visitor's initial http:// request can be intercepted before the HSTS header is ever seen. Preloaded domains are never requested over HTTP at all.

  • It removes the redirect hop. Browsers go straight to HTTPS for preloaded domains, saving a round trip on visits typed or linked as http://.

  • It is a strong trust signal. Major banks, government services and technology companies preload their domains, and security reviews look for it.

Effect on the health score

None. This is a suggestion and never deducts.

How to fix it

  1. Make sure every subdomain serves HTTPS. Preloading covers all of them, permanently from the browser's point of view.

  2. Send the full header from the bare domain (and, ideally, everywhere):

    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
  3. Redirect HTTP to HTTPS on the same host before redirecting anywhere else (http://example.com → https://example.com → https://www.example.com).

  4. Check and submit at hstspreload.org. It reports any remaining problem with the domain.

When to leave it alone

If you are not ready to commit every subdomain to HTTPS for the long term, do not add preload. Removing a domain from the list takes months to reach browsers.

Examples

Example 1: Preload-ready

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload

Passes.

Example 2: Everything except the token

Strict-Transport-Security: max-age=31536000; includeSubDomains

Suggested: missing preload.

Example 3: Asks for preload but would be refused

Strict-Transport-Security: max-age=86400; preload

Suggested, with the "would reject it" message: missing a one-year max-age and includeSubDomains.

Example 4: Any order, any case

Strict-Transport-Security: PRELOAD; IncludeSubDomains; Max-Age="63072000"

Passes: order does not matter, names ignore case and values may be quoted.

How PixyScan detects this

  1. Only HTTPS pages with a usable HSTS policy are checked. If the site sends no HSTS at all, that is "No Strict-Transport-Security header" instead.

  2. Checks the three header requirements of hstspreload.org: max-age of at least 31,536,000, includeSubDomains, and preload.

  3. Raises one suggestion per page listing every unmet requirement, in missingRequirements.

  4. Uses a different message when preload is present but the other requirements are not, because that header will be rejected on submission.

  5. Does not check the domain-level requirements (apex domain, HTTP redirect) — those are properties of the domain, not of a page's header.

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, INCLUDESUBDOMAINS and includesubdomains are 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=700 entirely, 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
missingRequirements String[] The unmet requirements: max-age of at least 31536000, includeSubDomains, preload
declaresPreload Boolean Whether the header already carries preload

Detection Dependencies

  • HTTP Response headers
  • HTTPS (the check only applies to pages served over HTTPS)

Further reading