HTTP Strict Transport Security is a response header that instructs the browser to contact your site only over HTTPS for a set period, refusing plain HTTP even if a user types it or a link forces it. It exists to defeat SSL stripping, the attack where a network adversary quietly downgrades an encrypted connection to an unencrypted one the victim never notices.

Without HSTS, the padlock protects a connection only after HTTPS is established. The moment before that, when a browser tries http:// first and expects a redirect, is a window an attacker on the same network can hijack. HSTS shrinks that window to nothing for repeat visitors and, with preloading, to nothing at all.

The attack HSTS stops

Picture a user on public Wi-Fi typing example.com. The browser sends http://example.com, expecting a 301 to HTTPS. An attacker running a rogue access point intercepts that request, keeps talking HTTPS to the real server, and serves the victim plain HTTP. The victim sees a normal-looking site with no padlock, and everything they type crosses the network in the clear. This is SSL stripping, demonstrated publicly by Moxie Marlinspike in 2009 and still viable wherever HSTS is absent.

The redirect from HTTP to HTTPS is not the fix. The attacker intercepts the request before the redirect ever runs. HSTS fixes it by making the browser refuse HTTP for your domain in the first place, so there is no plaintext request to hijack.

The header, decoded

Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
DirectiveMeaning
max-age=63072000Enforce HTTPS for this many seconds (two years) from the last visit
includeSubDomainsApply the rule to every subdomain, not just the exact host
preloadDeclare consent to be built into browsers' preload lists

Every time the browser gets this header over a valid HTTPS connection, it resets the clock. An active site effectively keeps the policy alive indefinitely. This rolling renewal is why max-age is a duration, not a fixed expiry date: a visitor who comes back weekly never lets the policy lapse, while one who visits once and disappears is protected only for the window you set. Preloading exists precisely for the visitors the header alone cannot reach in time.

One subtlety catches people out: the policy is scoped to the exact host that sent it unless includeSubDomains is present. A header served only from www.example.com does not protect example.com or shop.example.com. Because attackers strip whichever host is unprotected, and cookies often span the whole domain, partial coverage can be quietly useless. This is the operational reason the recommended value includes includeSubDomains despite the care it demands.

Choosing max-age

A short max-age weakens protection because the policy expires between visits. The preload list requires at least 31536000 (one year); two years (63072000) is the common production value. When first testing, some teams start with a small value like 300 to confirm nothing breaks, then raise it. That caution is reasonable, but do not ship a permanently tiny value: it defeats the point.

includeSubDomains: powerful and unforgiving

Adding includeSubDomains extends the HTTPS-only rule to anything.example.com. That is what you want, because an attacker can otherwise strip a forgotten subdomain and steal cookies scoped to the parent domain. The risk is operational: if a single subdomain, some legacy internal tool, only speaks HTTP, browsers that have seen the header will refuse to load it, with no override for the user.

Before enabling includeSubDomains, inventory every subdomain and confirm each serves valid HTTPS. There is no per-page exception. A missed intranet host or a staging box on HTTP can become unreachable for anyone whose browser has cached the policy.

The first-visit gap and preloading

HSTS is delivered by a header, which means the browser must have visited over HTTPS at least once to know the rule. The very first request from a brand-new browser, before it has ever seen your header, can still be plain HTTP. Preloading closes this last gap.

The HSTS preload list is a set of domains hard-coded into Chromium, Firefox, Safari and Edge as HTTPS-only from the very first request. To qualify (per hstspreload.org) your site must:

  • Serve a valid certificate on the base domain.
  • Redirect HTTP to HTTPS on the same host.
  • Serve the HSTS header on the base domain with max-age of at least one year, includeSubDomains and preload.
Preloading is difficult to undo. Removal requests are processed slowly and can take months to reach users because the list ships with browser releases. Only preload a domain you are certain will be HTTPS-only for the foreseeable future, across every subdomain.

Setting it correctly

Serve HSTS only over HTTPS; sending it on a plain-HTTP response is meaningless and browsers ignore it. In nginx, inside the HTTPS server block:

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

In Apache:

Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"

And ensure the redirect exists so the first-ever HTTP request lands on HTTPS in the first place:

server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}

Our redirect check verifies that step, and the HSTS check and preload-eligibility check confirm the header and its preload readiness.

Rolling it out safely

The reason HSTS earns a reputation for danger is that its enforcement is one-directional and cached on the client, where you cannot reach in and undo it. A certificate that expires, a subdomain that reverts to HTTP, a mistaken deployment, none of these can be papered over with a "proceed anyway" click, because HSTS deliberately removes that click. That strictness is the whole value of the header, and it is also why a careful rollout matters. A sensible sequence protects you from locking yourself out:

  1. Get HTTPS solid everywhere first, including every subdomain, with automated certificate renewal in place, before you send the header at all.
  2. Ship a short max-age, such as 300 seconds, without includeSubDomains, and confirm nothing breaks.
  3. Raise max-age gradually to one year and then two, watching for any subdomain or client that struggles.
  4. Add includeSubDomains once you have proven every host serves HTTPS.
  5. Add preload and submit only when you are confident the commitment is permanent.

Handled this way, HSTS is boring in the best sense: it sits in your configuration doing its job for years. The failures come from skipping the groundwork, sending includeSubDomains; preload on day one before an internal tool on HTTP has been dealt with, and then discovering the problem when a colleague's browser refuses to load it. The header is unforgiving by design; the rollout is where you build in the forgiveness.

Where HSTS sits in the stack

HSTS assumes you already have valid, current TLS; it enforces the use of encryption you have configured elsewhere. Pair it with the transport work in SSL and TLS explained, and treat it as one of the browser-enforced defences in HTTP security headers explained. On the user's side, the browser's HTTPS-Only mode offers similar protection from the client end, and framing it in context is the website security hub.

To confirm your header is live and well-formed, run the security headers checker or the deeper SSL/TLS checker. HSTS is a one-line change with outsized value, but only if the redirect and certificates behind it are solid first.

One historical note puts HSTS in perspective. Before it existed, the standard advice was simply "redirect HTTP to HTTPS," and for years that felt sufficient. SSL stripping showed it was not: the redirect itself travels over the very plaintext connection an attacker controls, so it can be rewritten or dropped before the browser ever reaches HTTPS. HSTS was designed specifically to close that gap by moving the decision into the browser, which will not even attempt HTTP for a known HSTS host. That lineage is why the two controls are complementary rather than redundant, the redirect handles the unavoidable first contact and the general case, while HSTS removes the plaintext attempt for every subsequent visit. Neither replaces the other, and a site that has one without the other is only partly protected. Configure both, verify both, and you have closed the downgrade path that plagued HTTPS for its first decade. The wider lesson is that encryption you offer is not the same as encryption you enforce: a site can support flawless TLS and still leak data on the one request that slipped through as plaintext. HSTS is the mechanism that turns "we support HTTPS" into "we require HTTPS," and that shift from optional to mandatory is the entire point. For a header that fits on a single line, few controls buy as much, provided you have done the unglamorous groundwork of getting every host onto valid, automatically renewed certificates first. Do that, ship the header, verify it, and one of the oldest attacks against encrypted sites simply stops working against yours.