Fix Missing HSTS | Add Strict-Transport-Security Guide
When a security scanner reports missing HSTS, it means your server is not sending the Strict-Transport-Security response header. Without it, browsers have no way to remember that your site must be loaded over HTTPS — even if every request redirects from HTTP. An attacker on a shared network can intercept the very first HTTP request before the redirect fires, exposing session cookies and credentials. This guide explains what HSTS is, why "missing HSTS" appears as a finding, and exactly how to add the header to Nginx, Apache, or a CDN safely without creating rollback problems.
What This Means
The Strict-Transport-Security header tells browsers to refuse plain HTTP connections to your hostname for a set period. The key directive is max-age, measured in seconds. A common production value is 31536000 (one year), but starting with a lower value such as 300 seconds lets you verify the deployment without locking users in for long. The includeSubDomains directive extends the policy to every subdomain — powerful, but dangerous if you have forgotten hosts that are not yet on valid HTTPS. The preload directive signals intent to join browser vendor preload lists, which hard-codes HSTS enforcement before any first visit; this is permanent and slow to reverse, so it should be treated as a final milestone rather than a first step. Safe rollout order: confirm HTTPS is healthy on all relevant hosts, deploy with a low max-age, raise it once confidence is established, then evaluate includeSubDomains and preload as separate decisions.
| Question | What to verify | Why it matters |
|---|---|---|
| Is HTTPS stable? | Certificates, redirects, and edge behavior | HSTS amplifies existing HTTPS problems if rollout is premature. |
| Are subdomains ready? | Mail, support, preview, or forgotten hostnames | includeSubDomains can break hosts you forgot to inventory. |
| Is max-age appropriate? | Temporary smoke-test versus strong production value | The wrong value can weaken protection or make rollback painful. |
| Is preload warranted? | Long-term operational readiness | Preload is powerful but much slower to reverse. |
Common Causes
Patterns worth checking first
- No HTTPS rollout process: The site is on HTTPS, but no one finalized browser-side enforcement.
- Subdomain uncertainty: Teams avoided HSTS because not every host was reviewed.
- Fear of lock-in: Preload and includeSubDomains felt risky without a staged plan.
How To Confirm It Safely
Confirmation steps
- Confirm the final HTTPS response is healthy and stable on the public hostname.
- Check certificates and redirects on any subdomains that matter to the business.
- Decide whether you are testing, hardening, or pursuing preload readiness.
- Verify that the header is absent or weaker than intended on the live response.
Fix Workflow
- Validate HTTPS operations first. Check certificates, redirect behavior, and important subdomains before adding HSTS.
- Choose the rollout stage. Start with a lower max-age if the environment still needs proof, then raise it once stable.
- Add stronger scope carefully. Only add includeSubDomains or preload after the broader estate is reviewed.
- Retest the live response. Confirm the final HTTPS response now carries the intended HSTS policy.
Implementation Examples
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"Rollout Risks
HSTS will not fix an unhealthy HTTPS deployment
It assumes HTTPS is already reliable.
- Repair certificate and redirect issues before promotion.
- Do not use HSTS to paper over transport drift.
includeSubDomains is risky if inventory is incomplete
Forgotten hosts are the classic HSTS rollout surprise.
- Inventory mail, support, preview, and legacy hosts.
- Use a staged approach if the estate is still messy.
Validation Checklist
Post-fix validation
- The final HTTPS response includes the intended HSTS header.
- Important subdomains remain healthy if includeSubDomains was used.
- Certificates and redirects stay stable after rollout.
- The HSTS Checker confirms the expected policy strength.
FAQ
What is HSTS and why is it missing?
HSTS is a browser security policy delivered via the Strict-Transport-Security response header. It is missing when your server never sends that header on HTTPS responses.
- Without HSTS, browsers re-check HTTP on every visit before following a redirect.
- An attacker on the same network can intercept the initial HTTP request before it redirects.
- Adding the header instructs browsers to skip HTTP entirely for the duration of max-age.
How do I add HSTS to Nginx?
Add the header inside your HTTPS server block in the Nginx configuration.
- In your ssl server block: add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
- Run nginx -t to validate the config, then reload: systemctl reload nginx
- Start with max-age=300 in staging to confirm no breakage before raising it in production.
What is the recommended HSTS max-age value?
The OWASP recommendation for production is at least one year (31536000 seconds), but the safest rollout starts lower.
- Use max-age=300 for initial smoke-testing — locks browsers in for only 5 minutes.
- Raise to max-age=86400 (one day) once HTTPS is confirmed stable across all entry points.
- Promote to max-age=31536000 for full production hardening and preload eligibility.
What is HSTS preloading and should I use it?
Preloading submits your domain to browser vendor lists so HTTPS is enforced even before the first visit. It is powerful but effectively irreversible on short timescales.
- Requirements: max-age of at least 31536000, includeSubDomains, and the preload directive.
- Removal from preload lists takes months and cannot be sped up — plan accordingly.
- Treat preload as a final milestone, not a starting point.
How do I add HSTS to Apache?
Enable mod_headers and set Strict-Transport-Security on your HTTPS VirtualHost.
- Ensure mod_headers is enabled: a2enmod headers && systemctl reload apache2
- Inside the SSL VirtualHost: Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
- Start with max-age=300 in staging, then raise once HTTPS redirects and certificates are stable on all hosts.
How do I enable HSTS on Cloudflare or a CDN?
Most CDNs terminate TLS at the edge — HSTS must be set on the response users receive from the CDN, not only the origin.
- Cloudflare: SSL/TLS → Edge Certificates → enable HTTP Strict Transport Security (HSTS) with a staged max-age.
- Other CDNs: add the header in the CDN response-header policy or edge rule that applies to HTTPS responses.
- Confirm the origin is not stripping or overriding the header after the CDN adds it.
How do I test whether my HSTS fix worked?
Always verify the live public hostname — not just origin config files — after publishing HSTS.
- Run the Vulnify HSTS Checker against your production URL to confirm max-age, includeSubDomains, and preload directives.
- Inspect the final HTTPS response in browser DevTools → Network → Response Headers.
- Retest after CDN or load-balancer changes — edge layers often override origin headers.