Headers

HSTS Not Working — Fix HTTP Strict Transport Security

HSTS only works when browsers receive it over HTTPS. If your site is sending it over HTTP, browsers ignore it. If max-age is 0 or very short, browsers discard it immediately.

The correct HSTS header

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

Common mistakes

Mistake 1 — Applied to HTTP responses

Browsers only process HSTS headers received over HTTPS. If your HTTP (port 80) virtual host also sets HSTS, browsers ignore it. The header must come from an HTTPS response.

# Nginx — correct: only set HSTS in SSL server block
server {
    listen 80;
    return 301 https://$host$request_uri; # no HSTS here
}

server {
    listen 443 ssl;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    # HSTS goes here, in the HTTPS block
}

Mistake 2 — max-age too short

If max-age is 0, browsers delete the HSTS record immediately. Very short values (less than a week) provide minimal protection. The recommended value is 31536000 (1 year).

Mistake 3 — Missing includeSubDomains

Without includeSubDomains, attackers can intercept connections to subdomains (like staging.example.com) and use them to attack users. Add it if all your subdomains support HTTPS.

Mistake 4 — Adding before HTTPS works fully

If you add HSTS before your SSL certificate is fully configured, users who visit will be locked out until max-age expires. Test HTTPS thoroughly before adding the header.

Config by stack

Nginx

server {
    listen 443 ssl http2;
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
}

Apache

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

Express

const helmet = require('helmet');
app.use(helmet.hsts({
  maxAge: 31536000,
  includeSubDomains: true,
  preload: true
}));

HSTS preload

The preload flag signals that you want your domain added to browsers' built-in HSTS lists — meaning HTTPS is enforced even on the first visit before the browser has ever seen your header. Submit at hstspreload.org once HSTS is working correctly.

Scan your security headers live → HeadersFixer