squirrelscan
GitHub

HSTS: what Strict-Transport-Security needs to be valid

A missing or short Strict-Transport-Security header leaves the first request downgradeable. squirrel reads the header and checks max-age.

What HSTS is

HTTP Strict Transport Security is a response header, Strict-Transport-Security, that tells a browser to use HTTPS for this hostname for a stated number of seconds and to refuse a plain HTTP connection during that window. Once the browser has seen it, a user typing example.com never issues the HTTP request that a redirect would otherwise answer.

The header only takes effect over HTTPS, and it is ignored on a plain HTTP response. includeSubDomains extends the policy to every subdomain, which will break any subdomain that is still HTTP-only. The first request before the header is ever seen is still downgradeable, which is what the preload list at hstspreload.org addresses.

What squirrel checks

The rule reads the response headers of the first crawled page and emits a single check named hsts:

  • Skipped when the crawl produced no pages. Message: No pages to check.
  • Info when that page’s URL is not HTTPS, since the header would be ignored. Message: HSTS not applicable - site not served over HTTPS.
  • Warn when the header is absent. Message: Missing Strict-Transport-Security header, with Add: Strict-Transport-Security: max-age=31536000 as the value.
  • Warn when the header is present but contains no max-age=<digits> directive, which makes it inert. Message: Malformed HSTS header — no valid max-age directive.
  • Warn when max-age=0, which instructs browsers to drop the policy. Message: HSTS disabled — max-age=0 tells browsers to stop enforcing HTTPS.
  • Warn when max-age is below 15768000 seconds, six months. Message: HSTS max-age too short (30 days), with the age rounded to whole days.
  • Pass otherwise. Message: HSTS configured correctly, with the raw header as the value.

Severity is warning and the scope is site-wide, so the verdict comes from one page’s headers rather than from every page.

On a local or private-network host, such as localhost or 192.168.1.10, the rule is skipped and left out of the score, because a dev server is not the production edge. See Transport and delivery rules do not apply.

How to fix it

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

Set the header on the HTTPS origin, not on the HTTP redirect. Start with a short max-age while you confirm that every subdomain serves HTTPS, then raise it to a year. The preload token is consent, not enrolment: the hostname joins the browser preload list only after you submit it at hstspreload.org and it is accepted. Getting an accepted entry removed again takes months, so treat submission as the hard-to-reverse step.

Rule IDsecurity/hsts
CategorySecurity
ScopeSite-wide
Severitywarning
Weight6/10

Enable / disable

Disable this rule

[rules]
disable = ["security/hsts"]

Disable all Security rules

[rules]
disable = ["security/*"]

Enable only this rule

[rules]
enable = ["security/hsts"]

enable replaces the default ["*"], so this is the only rule selected. Leave disable out: a rule that matches any disable pattern is skipped even when enable names it. Only an enabled setting under a rule’s own [rule_options] table overrides both lists.

Security findings ship in every audit next to the SEO, performance and agent experience rules. See Website security scan with AI for how an agent works through a report.

References

Check your site

Run squirrel audit https://example.com and open the Security section of the report. The Strict-Transport-Security header and its max-age are reported with the exact value your server sent. Signed-out local audits need no account and cost nothing. Signed-in audits spend credits.

Type to search…

↑↓ navigate↵ openesc close