Security Headers

How to fix a missing HSTS header

Quick fix guide with step-by-step instructions. Barrion detects this finding in your scans; use this page to remediate it.

What it is

HSTS (HTTP Strict Transport Security) is a response header that tells browsers to only connect to your site over HTTPS for a set period. Once set, the browser will refuse to connect via HTTP, which prevents downgrade attacks and cookie hijacking on the first visit.

Why it matters

Without HSTS, the first request to your site (or after the header expires) can be intercepted and downgraded to HTTP by an attacker. HSTS ensures that after the first secure visit, all subsequent requests use HTTPS only.

How it is exploited

On the first request to your site the user types example.com, so the browser opens HTTP first. An attacker on the same Wi-Fi sees the request, replies with a fake HTTP page, and harvests credentials or strips the redirect to HTTPS (sslstrip). HSTS skips that HTTP hop for return visits.

How to fix it

  1. Choose max-age. Set max-age to at least 31536000 (1 year). Include includeSubDomains if all subdomains use HTTPS. Add preload if you want to submit your site to the browser HSTS preload list.
  2. Add the header on your server. Send the Strict-Transport-Security response header on every HTTPS response. Use your web server config (Nginx, Apache) or application middleware (Node, Next.js).
  3. Ensure HTTP redirects to HTTPS first. Before enabling HSTS, make sure all HTTP traffic redirects to HTTPS (301). Otherwise users may never receive the HSTS header.
  4. Verify. Run a scan or use your browser dev tools (Network tab) to confirm the Strict-Transport-Security header is present on your HTTPS responses.

Examples by platform

Nginx

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

Apache

Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"

Node.js (Express)

app.set('trust proxy', 1);
app.use((req, res, next) => {
  if (req.secure) {
    res.setHeader('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload');
  }
  next();
});

Next.js (next.config.js)

// In next.config.js headers:
headers: [
 { key: "Strict-Transport-Security", value: "max-age=31536000; includeSubDomains; preload" }
]

How to verify the fix

Confirm the Strict-Transport-Security header is present in HTTPS responses:

curl -sI https://example.com | grep -i strict-transport-security

References

Related reading

Frequently asked questions

What max-age is recommended for HSTS?
Use at least 31536000 seconds (1 year). Shorter values are fine while testing, but the HSTS preload list requires 1 year or more.
Should I enable HSTS preload?
Preload is worth it if you control all subdomains and serve every one over HTTPS. It removes the trust-on-first-use gap, but removal from the preload list is slow, so be sure first.
Will HSTS break my staging or local dev?
It can. Browsers cache HSTS per hostname, so once a host has sent the header it will refuse plain HTTP. Use a separate hostname for staging, or skip HSTS on localhost and non-production environments.
How do I remove HSTS once it is set?
Send the header with max-age=0 over HTTPS so visiting browsers expire the cached policy. Users who never come back over HTTPS keep the old policy until it expires on its own.

Check your site for this finding.

Run Barrion's free security headers check to see if this applies to your app, with a full report and remediation steps.