HSTS: What It Is and How to Enable HTTP Strict Transport Security
A plain guide to the Strict-Transport-Security header: what our HSTS check tests, why it fails, and a safe rollout from a short max-age to preload.
Free to use 11 min read Updated Oct 2026
HSTS in one minute: the quick answer
Quick answer: HSTS is a response header called Strict-Transport-Security that tells a browser to use only HTTPS for your domain for a stated number of seconds. Our report checks one thing: whether the page it fetched came back with that header. Without it, a repeat visitor's first request can still leave the browser as plain HTTP.
- Pass: the page we tested answered with a Strict-Transport-Security header carrying any non empty value.
- Fail: no such header came back, or the request failed or took longer than 3 seconds.
- Not tested: the max-age size, includeSubDomains and preload status, so even max-age=0 passes.
- Fix: add one header line on the HTTPS side of your server and start with a short max-age.
What is HSTS and how does the Strict-Transport-Security header work?
The name expands to HTTP Strict Transport Security. It is an opt in policy, defined by the IETF in RFC 6797, that arrives in an ordinary response header. The browser stores the policy per host and obeys it until max-age runs out. Every visit that returns the header restarts the timer, so a busy site keeps the policy alive indefinitely.
Below is the complete form, a destination rather than a day one value. The RFC 6797 header syntax gives the exact grammar.
Strict-Transport-Security: max-age=63072000; includeSubDomains; preloadOnce a browser has stored the policy, it rewrites any http:// request for that host to https:// before a single byte leaves the machine. It also removes the click through button on certificate errors, so the "Your connection is not private" screen cannot be dismissed. One limit matters: the header only counts over HTTPS, so browsers ignore it on plain HTTP and your redirect still has to exist.
The three directives
- max-age. This directive sets the number of seconds the browser should remember the rule. You will usually see 31536000 (one year) or 63072000 (two years).
- includeSubDomains. This directive extends the rule to every subdomain, so each one must serve valid HTTPS or it becomes unreachable.
- preload. A signal that you agree to be listed in the browser built in list. It does nothing by itself.
What the browser does with it
Chrome, Firefox, Safari and Edge keep the host name and expiry in their own cache, keyed by domain name alone. Typed addresses, bookmarks and old http links are then upgraded locally. It belongs with Content-Security-Policy, X-Content-Type-Options and Referrer-Policy among the security headers.
Why HSTS matters: SSL stripping and the first visit problem
Picture a visitor on cafe Wi-Fi typing example.com. The first request goes out as plain HTTP, and an attacker in the middle answers it. That man-in-the-middle attack keeps the victim on HTTP while talking HTTPS to the real server. Moxie Marlinspike demonstrated this SSL stripping in 2009 with sslstrip, a textbook protocol downgrade attack.
Logins and form posts travel in clear text, and a cookie lacking the secure cookie flag can be read and replayed, the classic route to cookie hijacking. After one good visit the browser stops making that plain HTTP request while max-age lasts, so the stripping has nothing to grab.
One gap remains: the very first visit from a browser that has never seen your header is still exposed. That trust on first use weakness is why the preload list exists. On the SEO side, HSTS is not a ranking factor. HTTPS is a light signal, and HSTS only removes one redirect hop for returning visitors, as the SSL and SEO ranking guide explains.
How SEO Site Checker tests HSTS
Our report sends one HEAD request to the address you submitted. If you typed no scheme, we add https:// and a trailing slash. The request has a 3 second limit, uses the default Python requests user agent and follows no redirects, so only the first response is judged.
Exact rule: the check passes when that single response contains a Strict-Transport-Security header with any non empty value. It fails when the header is missing or the request errors, and there is no warning state.
| What the report shows | What it means | What to do next |
|---|---|---|
| Green check | The first response had the header. | Read the value, then raise max-age in stages. |
| Red alert, header missing | The server answered without that line. | Add the header to every HTTPS response. |
| Red alert, request error | Timeout, DNS error, refused connection or certificate error. | Fix DNS, the firewall or the certificate first, then retest. |
What a pass and a fail mean here
A pass means the header was present on that one response, not that the policy is strong. A fail means we saw no header on that exact response. Security scanners word the same finding as HSTS missing from HTTPS server, and the cause is identical: the response they fetched had no such line.
Edge cases that cause a surprise fail
- Header only on the final page. If your address redirects, for example from the apex domain to www, we read the redirect response and stop, so a header that appears only afterward is invisible to us. Browsers honor the header on a 301 or 302 over HTTPS, so send it from the virtual host or server block that returns the redirect.
- Blocked or slow requests. A firewall that rejects HEAD requests or the Python user agent causes a fail, and so does a reply slower than 3 seconds.
- An http:// address. We test the address as typed. This normally fails because few servers send the header on port 80, and if one did, the pass would mean nothing, since browsers ignore it there. Always submit the https address.
Test the address your visitors land on, then rerun the SEO score checker after any change.
How to check HSTS on any website
- Use the terminal. Run the command below on the exact address you submitted. The
-sIflags send a HEAD request without following redirects, as our check does. - Use the browser. Open DevTools, choose Network, reload, click the first document request, and read Strict-Transport-Security under Response Headers on the Headers tab. A 307 Internal Redirect with Non-Authoritative-Reason: HSTS means the browser upgraded an http address from its own cache, so use a private window or curl to see what the server sends.
- Run our report. Start the SEO score checker and read the HSTS line in the Security and Server group. If the certificate is in doubt, read the SSL checker and HTTPS test guide first.
curl -sI https://example.com/Look for a line such as strict-transport-security: max-age=31536000; includeSubDomains. Lower case is normal over HTTP/2.
How to enable HSTS safely, step by step
Three things must be true before you start. The host needs a valid SSL certificate (a free Let's Encrypt one is fine), and so does every subdomain if you will use includeSubDomains, which the HTTPS encryption check helps confirm. Plain HTTP needs a 301 redirect to HTTPS, as in our guide to http to https redirects, and key pages need to be free of mixed content, so run the mixed content test first.
- Start tiny. Send
max-age=3600, which is one hour, and browse the whole site. Anything that breaks stops mattering within the hour. - Step up slowly. Keep each value for at least its own length: a day at 86400, a week at 604800, checking logs and support messages before each increase.
- Settle on a long value. After a few good weeks use 31536000, or 63072000 if you plan to preload, though one year already meets the list minimum.
- Add includeSubDomains late. First list every subdomain, including forgotten ones like staging, mail and shop. A DNS record lookup shows where each name points but cannot list subdomains for you.
- Add preload last. Read the preload section first, because leaving the list is slow.
HSTS header examples for Apache, Nginx, IIS and Cloudflare
Each example uses a one year max-age with includeSubDomains. Apply it on the HTTPS virtual host or server block only, then reload and re-run curl. The hosting checker shows who hosts your domain, which tells you whether to edit files or a panel. The MDN header reference lists syntax and browser support.
Apache and LiteSpeed
In Apache the line goes inside the 443 virtual host, and Apache mod_headers must be on (a2enmod headers on Debian style systems) before you reload. Use always, because a plain Header set fires only on 2xx responses and misses redirects and errors. On LiteSpeed, use the virtual host or the panel's header settings if curl shows nothing.
<VirtualHost *:443>
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
</VirtualHost>Nginx
Nginx wants it in the server block that listens on 443, with always so error pages carry it too. The trap is inheritance: an add_header inside a location block replaces the inherited ones, so repeat the line there.
server {
listen 443 ssl;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
}IIS
From IIS 10 version 1709 onward, the hsts element sits on the site and takes the attributes shown below. It lives in applicationHost.config, which you edit directly or with appcmd, so a copy in web.config does nothing. Older versions need an outbound URL Rewrite rule limited to HTTPS.
<site name="Contoso" id="1">
<hsts enabled="true" max-age="31536000" includeSubDomains="true" redirectHttpToHttps="true" />
</site>Cloudflare
The Cloudflare HSTS setting sits under SSL/TLS, then Edge Certificates. Its Max Age Header menu offers Disable or 1 to 12 months, so the one hour test is not possible: confirm every hostname behind Cloudflare works over HTTPS, then pick 1 month. The menu stops at twelve months, which meets the preload minimum but cannot give you 63072000. Switches for subdomains (includeSubDomains), Preload and No-Sniff Header sit beside it. Never remove HTTPS before you turn the policy off and wait out the max age, or visitors stay locked out until it expires.
WordPress and hosting panels
WordPress core has no switch, so a rule in the root .htaccess file on Apache or LiteSpeed is the usual route. Keep it above the WordPress block so updates leave it alone.
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains" env=HTTPS
</IfModule>HAProxy takes the same value through http-response set-header, and CloudFront through a CloudFront response headers policy.
HSTS preload: what it is and when to use it
The HSTS preload list ships inside Chromium, and Firefox, Safari and Edge consume it as well, so even the first visit is HTTPS only. Submission goes through hstspreload.org, which tests your header first, and its hstspreload.org deployment advice is worth reading. That site itself recommends HSTS but not preloading for most sites. Preload suits high value domains that can commit every subdomain to HTTPS for good.
Requirements: a valid certificate, an HTTP to HTTPS redirect on the same host, every subdomain served over HTTPS, and a header with max-age of at least 31536000 plus both includeSubDomains and the preload directive. Any HTTPS redirect must carry the header too.
Preload removal takes months to reach users, so commit only when HTTPS is permanent for the whole domain.
Common HSTS mistakes and how to undo them
Sending the header on port 80 only does nothing, because browsers ignore it over plain HTTP. Sending it only on the home page leaves a visitor who lands on a deep page from search without a policy until a response carrying the header arrives, and our check only sees the address you submitted. Put it on every HTTPS response.
Jumping straight to two years with includeSubDomains is how people learn, a month later, that a forgotten staging subdomain has no certificate. The symptom is an error page with no proceed anyway link, while curl against that host shows a certificate or connection error.
How to back out: send
max-age=0over HTTPS, which clears the policy for returning visitors. In Chrome, open chrome://net-internals/#hsts and use Delete domain security policies. Preloaded entries stay.
If the header vanishes from redirects or error pages, check the server. On Apache a missing always flag drops it from both, and on Nginx an add_header inside a location block silently discards the server level one. The tell is a header that shows on the home page in curl but not on a 301 or a 404.
Watch certificate expiry as well. A lapsed certificate under an active policy shows NET::ERR_CERT_DATE_INVALID (or NET::ERR_CERT_AUTHORITY_INVALID for an untrusted one) with no proceed anyway link, so monitor renewals.
How to Fix
The short version, the same steps you see when you click How to Fix in your report.
Why this fails: the page we tested did not return a Strict-Transport-Security header, so browsers are not told to stay on HTTPS. The header belongs on HTTPS responses only, because browsers ignore it over plain HTTP.
Before you switch it on: make sure you have a valid certificate, a redirect from http to https, no mixed content and a small start value. Remember that includeSubDomains reaches every subdomain, so leave it out until you have checked each one.
How to fix it:
- Open the HTTPS virtual host (Apache or LiteSpeed) or the server block that listens on 443 (Nginx).
- Add the header line below with max-age=3600 as the first value.
- Test the configuration, then reload the server.
- Raise max-age in stages, keeping each value for its own length: 3600, then 86400, then 604800, then 31536000 or more after a few good weeks.
# Apache or LiteSpeed (needs mod_headers)
Header always set Strict-Transport-Security "max-age=3600"
# Nginx (then run nginx -t and reload)
add_header Strict-Transport-Security "max-age=3600" always;- IIS: on version 10 1709 or later, add this inside your site element in applicationHost.config:
<hsts enabled="true" max-age="3600" includeSubDomains="false" />. Older IIS needs an outbound URL Rewrite rule limited to HTTPS. - Cloudflare: open SSL/TLS, then Edge Certificates, turn on HSTS and start with the shortest option, 1 month, only after every hostname behind Cloudflare works over HTTPS. The subdomain switch is called Apply HSTS policy to subdomains (includeSubDomains).
- WordPress: add the Apache line inside
<IfModule mod_headers.c>in the root .htaccess, outside the WordPress block. If curl still shows no header, mod_headers is probably off or your host ignores .htaccess header rules, so use the host panel or ask support. A 500 error means a typo in the line, so remove it and re-check the quotes.
curl -sI https://yourdomain.com/Re-test: run the command above on the exact address you tested and confirm a Strict-Transport-Security line appears, then run our free report again. Our check reads the first response without following redirects, so the header has to be on that response. The MDN Strict-Transport-Security reference covers every directive.
Frequently Asked Questions
What does HSTS do?
Is HSTS necessary if I already redirect HTTP to HTTPS?
What max-age should I use for HSTS?
Should I use includeSubDomains and preload?
Does HSTS help SEO?
Why does the report say HSTS is missing when I added the header?
How do I enable HSTS in WordPress?
How do I clear HSTS in Chrome?
See how your site scores right now
Paste your address into the free report, then use the How to Fix button on anything that needs work.
Free to use. Results in under a minute for most sites.