securityaudit.website
Fix guide

HSTS Explained — Stop SSL Stripping with Strict-Transport-Security

You redirect HTTP to HTTPS, so you're safe — right? Not quite. The very first request a browser makes is often plain HTTP, and that single unencrypted hop is enough for an attacker on the same network to perform SSL stripping: silently keeping the victim on HTTP while talking HTTPS to your server. HTTP Strict Transport Security (HSTS) closes that window.

What HSTS does

When a browser sees an HSTS header over HTTPS, it remembers — for the duration you specify — that this domain must only be reached over HTTPS. After that, the browser upgrades every request to HTTPS before it leaves the device, so there's no HTTP hop left to intercept.

The header

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

Three parts:

  • max-age — how long (in seconds) the browser enforces HTTPS. 63072000 is two years, the recommended value. Anything under six months is considered weak.
  • includeSubDomains — applies the rule to every subdomain. Make sure all of them serve HTTPS first, or you'll lock yourself out of HTTP-only subdomains.
  • preload — opts you into the browser preload list (see below).

Roll it out carefully

HSTS is sticky by design — that's the point — but it also means a mistake is sticky. Deploy in stages:

  1. Confirm your whole site and all subdomains work over HTTPS.
  2. Start with a short max-age, e.g. 300 (5 minutes), and verify nothing breaks.
  3. Raise it to a day, then a week, then the full two years.
  4. Only add includeSubDomains once every subdomain is HTTPS-ready.

The preload list

preload lets you submit your domain to a list baked directly into browsers, so HTTPS is enforced even on a user's very first visit. Submit at hstspreload.org. Be deliberate: removal from the list is slow, so only preload a domain you're certain will stay HTTPS-only forever.

Setting the header

# nginx — only over HTTPS
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# Apache
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" env=HTTPS

Note the env=HTTPS guard on Apache — never send HSTS over plain HTTP; the spec says browsers ignore it there, and it can cause confusion.

Verify

Re-run your audit. A pass means HSTS is present with a strong max-age. If you see a warning about a short max-age, bump it up once you're confident the rollout is stable.

Want to know if your site has this issue?
Run a free audit →

More guides