How do I fix a missing Strict-Transport-Security header?
A missing Strict-Transport-Security header lets a browser keep contacting your host over plain HTTP, where a network attacker can read or alter traffic before any redirect runs. The Strict-Transport-Security response header, usually abbreviated HSTS, tells the browser that the host should only be accessed using HTTPS and that future HTTP attempts must be upgraded automatically. The fix is to send the header over HTTPS once your certificate is valid.
Why this matters
Without HSTS the first request a user makes can be intercepted by a manipulator in the middle, who can strip the upgrade or serve a fake login page. MDN explains that once a host is remembered as an HSTS host, the browser connects over HTTPS and refuses to let the user bypass certificate errors, which closes that downgrade window. The header does not affect the current response; it changes how future requests are made, and it identifies a host by domain name only, since an IP address cannot be an HSTS host. That creates an inherent first-visit gap: the browser can enforce the policy only after it has seen the header at least once, so the initial plaintext request is still exposed unless the domain is on a browser preload list. You still need a permanent redirect such as 301 from HTTP to HTTPS, and that redirect must not itself include the HSTS header, because the header must be sent over HTTPS only. Every subdomain should also send its own header, because a browser may contact a subdomain before it ever sees the apex response.
How to fix it
Emit the header only on HTTPS responses. max-age is the number of seconds the browser remembers the policy, includeSubDomains extends it to subdomains, and preload signals intent to join browser preload lists and requires a max-age of at least 31536000 with includeSubDomains present. The examples set 63072000 seconds, about two years, matching the value in the Next.js headers documentation and satisfying the preload requirement of at least one year. Keep the deployment sequence in mind: enable HTTPS everywhere first, then add the redirect, then add HSTS, because a wrong policy can lock users out of an HTTP-only subdomain. The snippets use Nginx add_header, Apache Header always set, Express res.set, the Next.js headers function, a Cloudflare Pages _headers file, and a Cloudflare Worker Response.
# Nginx
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
# Apache
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
// Express
app.use(function (req, res, next) {
res.set('Strict-Transport-Security', 'max-age=63072000; includeSubDomains; preload');
next();
});
// next.config.js
module.exports = {
async headers() {
return [
{
source: '/:path*',
headers: [ { key: 'Strict-Transport-Security', value: 'max-age=63072000; includeSubDomains; preload' } ],
},
]
},
}
# Cloudflare Pages public/_headers
/*
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload
// Cloudflare Workers
export default {
async fetch() {
return new Response('ok', {
headers: { 'Strict-Transport-Security': 'max-age=63072000; includeSubDomains; preload' },
});
},
};
How to verify the fix
Confirm the header on an HTTPS response, not on the HTTP redirect. The curl command below sends a HEAD request to the https URL, and DevTools shows the same value on the document response under Network. If you intend to preload, verify that max-age is at least one year and that includeSubDomains is present before you submit the domain, because the preload list is difficult to reverse.
curl -sI https://example.com/ | grep -i strict-transport-security
Automated check
The Auditor requests your page and raises a Missing Strict-Transport-Security header finding at High severity when the header is absent. It checks for presence on the response it receives, so make sure the URL it scans is the HTTPS endpoint; the header is meaningless on plain HTTP and browsers ignore it there.
Scan your app free →