HSTS: the Strict-Transport-Security header
Tells the browser to use HTTPS for your domain for a set time, so a network attacker cannot downgrade visitors to plain HTTP.
Updated 3 November 2026
Check your site's HSTS header
We send one normal request (following redirects) and grade the Strict-Transport-Security header on the final response.
Recommended value
Strict-Transport-Security: max-age=31536000; includeSubDomains
| Value / directive | What it does |
|---|---|
| max-age=<seconds> | How long the browser remembers to use HTTPS only. One year (31536000) is the common minimum; two years (63072000) is what the preload list suggests. |
| includeSubDomains | Applies the rule to every subdomain too. Only add it once every subdomain serves HTTPS. |
| preload | Signals consent to be added to the browser-shipped preload list. Has no effect until you submit the domain at hstspreload.org. |
| max-age=0 | Tells browsers to forget the HSTS policy. Use it to back out. |
What HSTS stops
When someone types example.com or follows an old http:// link, the first request goes out unencrypted. An attacker on the same Wi-Fi can answer that request and keep the victim on HTTP (SSL stripping), reading passwords and session cookies. After a browser has seen your HSTS header once over HTTPS, it rewrites every later http:// request to https:// itself and refuses to let the user click through certificate errors.
Browsers ignore the header on plain-HTTP responses, so it has to be sent over HTTPS. Redirect HTTP to HTTPS first, then add HSTS to the HTTPS responses.
Roll it out in steps
- Start with a short max-age, such as
max-age=300, and check nothing breaks. - Raise it to a week, then a month, then
max-age=31536000. - Add
includeSubDomainsonce you have confirmed every subdomain (including internal tools, mail web front-ends and old marketing sites) works on HTTPS. - Consider preload only when you are sure, because removal takes months.
The preload list
Preloading puts your domain into the HSTS list compiled into Chrome, Firefox, Safari and Edge, which protects even the very first visit. The requirements at hstspreload.org are: a valid certificate, a redirect from HTTP to HTTPS on the same host, HTTPS on all subdomains, and an HSTS header on the base domain with max-age of at least one year (31536000), includeSubDomains and preload. Getting off the list again means submitting a removal request and waiting for browser releases, so never preload a domain whose subdomains you do not fully control.
How to set Strict-Transport-Security
nginx
# inside your server { } block
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
Apache
# .htaccess or virtual host (needs mod_headers)
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
Laravel middleware
// app/Http/Middleware/SecurityHeaders.php
public function handle(Request $request, Closure $next): Response
{
$response = $next($request);
$response->headers->set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
return $response;
}
// bootstrap/app.php
->withMiddleware(function (Middleware $middleware) {
$middleware->append(\App\Http\Middleware\SecurityHeaders::class);
})
If a proxy or load balancer terminates TLS, make sure Laravel trusts it (TrustProxies) so the app knows the request was HTTPS.
Express (Node.js)
app.use((req, res, next) => {
res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains');
next();
});
// or: app.use(require('helmet')()) for a sensible default set
Cloudflare
SSL/TLS → Edge Certificates → HTTP Strict Transport Security (HSTS) → Enable. Set Max Age to 12 months, enable Include subdomains only when every subdomain is HTTPS, and leave Preload off unless you are submitting to hstspreload.org.
In nginx, an add_header inside a location block discards every add_header inherited from the server block. Repeat your security headers there, or use an include file. The always flag also sends the header on error responses.
Common mistakes
- Sending HSTS only on the HTTP response or only on the redirect. Browsers only accept it over HTTPS.
-
Adding
includeSubDomainswhile an internal tool or old subdomain is still HTTP-only. It becomes unreachable for every visitor who has the policy cached. -
Adding
preloadon a staging or shared domain. Once shipped in browsers it is very slow to undo. - A max-age of a few minutes left in production after testing. That gives almost no protection.
- Setting HSTS both in nginx and the app with different values. Browsers process only the first header they see.
FAQ
What max-age should HSTS use?
At least 31536000 seconds (one year). The preload list requires one year and recommends two (63072000). Start lower while testing, then raise it.
Does HSTS work on HTTP?
No. Browsers ignore Strict-Transport-Security on plain-HTTP responses. Redirect to HTTPS and send the header on HTTPS responses.
Should I use includeSubDomains?
Yes, once every subdomain serves HTTPS. It stops attackers using an HTTP subdomain to set or read cookies scoped to the parent domain.
How do I turn HSTS off?
Send Strict-Transport-Security: max-age=0 over HTTPS. Browsers that see it forget the policy. Preloaded domains also need a removal request at hstspreload.org.
Related guides
Headers are one of 30+ things attackers check
The free StackShield scan also checks for exposed .env files, debug mode, leaked tooling, weak TLS and more, from the outside, in about a minute.
Run a free scan