Category: Web Server and InfrastructureUpdated 2026-10-18

HSTS

HSTS (HTTP Strict Transport Security) is a policy defined in RFC 6797 and sent as the Strict-Transport-Security response header. It tells a browser to contact a site only over HTTPS for a set number of seconds, upgrading http links automatically and refusing to let users click through certificate errors.

What the Strict-Transport-Security header does

HSTS is specified in RFC 6797. A server sends the header over HTTPS, and the browser remembers the host as HTTPS-only. The header has a required max-age directive, the number of seconds to remember the policy, and an optional includeSubDomains directive that extends the policy to all subdomains.

For a known HSTS host, MDN explains that the browser rewrites http URLs to https, turning port 80 into 443, and if a certificate problem occurs the user cannot proceed past it. Browsers ignore the header when it arrives over plain HTTP, and an IP address cannot be an HSTS host.

Each response with the header resets the expiry to now plus max-age. One weakness remains: nothing is protected until the browser has made one secure visit and received the header. preload is not part of the RFC. It is a separate browser-maintained list that closes that first-visit gap.

How to set up and test HSTS in nginx

Send the header only from the HTTPS server block, and keep the HTTP block as a plain redirect without it, as MDN advises. Use the always parameter, because per the nginx documentation add_header otherwise skips responses such as 404 and 500.

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

server {
    listen 443 ssl;
    server_name example.com;
    # Stage 1: five minutes. Raise it later, see the rollout section.
    add_header Strict-Transport-Security "max-age=300; includeSubDomains" always;
}

Validate and reload with sudo nginx -t && sudo systemctl reload nginx, then test from another machine:

curl -sI https://example.com/ | grep -i strict-transport-security
curl -sI https://example.com/no-such-page | grep -i strict-transport-security
curl -sI http://example.com/

The first two should print the header. The third should show a 301 redirect and no header.

Rollout steps and the risks of long max-age or preload

A mistake is slow to undo. MDN says that max-age=0 takes effect only after the browser makes a secure request, so you cannot cancel HSTS over insecure HTTP. If a subdomain or certificate breaks while a long policy is cached, affected users are locked out with no click-through.

The hstspreload.org guidance gives this order:

  1. Confirm every subdomain works over HTTPS, including internal ones that are not public.
  2. Deploy max-age=300; includeSubDomains, then raise to 604800 (one week), then 2592000 (one month).
  3. At each stage fix any breakage and wait the full max-age before moving on.

Preloading needs a valid certificate, an HTTP to HTTPS redirect, max-age of at least 31536000, plus includeSubDomains and preload. The site warns that inclusion cannot easily be undone and removal can take months to reach users. Do not add preload unless every subdomain will support HTTPS long term.

Where HSTS fits in hardening

HSTS builds on working TLS: it does not replace a good certificate, modern protocol settings or renewal. It is one of the security headers, enforced by the browser rather than the server, so it only helps visitors who already reached you securely, or whose browser has you on a preload list.

The baseline snippet in the nginx security guide shows the end state, a two year max-age with preload. Reach it in the stages above rather than pasting it on day one. Because HSTS turns certificate errors into hard failures, an expired certificate takes the site down for everyone, so monitor expiry, as the Let's Encrypt 45-day certificates article explains. Add log review from the monitoring guide, and tick the item off the VPS security checklist. A reverse proxy or CDN in front of the site must send the header too.

Related terms: Security headers, TLS / SSL, nginx, ssl_protocols, Reverse proxy.

Frequently asked questions

What does HSTS stand for?

HSTS stands for HTTP Strict Transport Security. It is a policy from RFC 6797, delivered in the Strict-Transport-Security response header, that makes browsers use only HTTPS for a host.

What max-age should I use for HSTS?

Start with a short value such as 300 seconds and raise it in stages to one week and one month, as hstspreload.org suggests. Preloading requires at least 31536000 seconds (one year).

How do I check that HSTS is working?

Run curl -sI https://example.com/ and look for the Strict-Transport-Security header, and repeat it on a missing page to confirm the always parameter works. The plain HTTP URL should only redirect and not carry the header.

Can I turn HSTS off once it is set?

Send max-age=0 over HTTPS, but per MDN it takes effect only after each browser makes a secure request. Removal from the browser preload list can take months, so do not preload casually.

Does HSTS protect the first visit to a site?

No. The browser learns the policy only after a secure response, so the first request can still be attacked. Preloading mitigates this, according to MDN.

Want to see where your own server stands?

Run the free, read-only server check, or open the Security Lab and watch the detect, contain, recover, verify loop in action.

Get your free server checkOpen the Security Lab