dotvitals

HTTP Strict Transport Security is not in force

HighConfirmedQuick winweb.security-headers.hsts-missing

What this check looks for

Browsers are not told to always use the secure version of your site. The first visit of every day can still be made over plain HTTP, where it can be intercepted.

Why it matters

A redirect from HTTP to HTTPS only helps after the insecure request has already been made — and that request is the one an attacker on the same network intercepts. HSTS removes it: the browser refuses to make it at all.

When the check passes, your report says: “Browsers are told to reach this site over HTTPS and nothing else”.

What it costs your score

When this check fails it removes 25 points from your Web security score, before the status, confidence and repeat multipliers are applied. Web security carries a weight of 8 in the overall score.

It shares the web-security.hsts family ceiling of 28 points: however many findings that family produces, together they cannot remove more than that from Web security. One underlying problem showing up in several places is still one problem.

Severity
high
Default confidence
confirmed
Status when triggered
fail
Deduction
25 points
Family cap
web-security.hsts · 28
Category
Web security
Module
Web security headers
Fix owned by
user
In the ruleset since
2026.09

How the whole score is calculated

How to fix it

Send Strict-Transport-Security on the HTTPS response, with a real lifetime.

Without it, the first request of a visit is plaintext and interceptable.

  1. Confirm every hostname you serve already works over HTTPS — HSTS makes the decision hard to reverse.

  2. Start at max-age=300 and confirm nothing breaks.

  3. Raise it to max-age=31536000 once you are confident.

  4. Add includeSubDomains only when every subdomain has a valid certificate.

How to confirm it worked

  • curl -sSI https://‹host›/ | grep -i strict-transport-security — expect a max-age of at least 31536000

The configuration to publish
Strict-Transport-Security: max-age=31536000; includeSubDomains

A named slot like ‹domain› — and the braces left in the configuration below — is filled in with your own values when this rule appears on a report.

Remediation by platform

nginx
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
Apache
<IfModule mod_headers.c>
	Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
</IfModule>
Caddy
header {
	Strict-Transport-Security "max-age=31536000; includeSubDomains"
}
Cloudflare
Rules → Overview → Create rule → Response Header Transform Rule → Set static → Strict-Transport-Security = max-age=31536000; includeSubDomains
(Dashboard path as at 2026-09; Cloudflare reorganised these pages and may again.)
  • The always flag matters: without it nginx omits the header on error responses, which are exactly the ones an attacker aims for. Note also that any add_header in a location block discards every add_header inherited from the server block.

  • Shown because the response identified Apache. Put it in the virtual host rather than .htaccess: .htaccess is re-read on every request, and Header always set there runs too late for responses the server generates itself.

  • A Transform Rule adds the header at Cloudflare's edge, so it applies only to responses that reach visitors through Cloudflare. An origin that is reachable directly — by IP, or through a DNS record that is not proxied — still serves the response measured here without it. Setting it at the origin covers both paths.

Technical detail

‹detail›

RFC 6797 §6.1 requires a max-age directive; a header without one, or with max-age=0, is ignored — max-age=0 is in fact the documented way to *withdraw* a policy. §7.2 requires browsers to ignore the header entirely when it arrives over plain HTTP, so it must be set on the HTTPS response. This rule therefore fails for a header that is present but inert, not only for one that is absent.

Standards and references

Test this on your domain

Run the check that produces this finding, on its own, against any domain.

Open the web security headers checkerBuild the fix

Other web security headers checks