networking ssl_tls ai_generated true

Strict-Transport-Security header missing or misconfigured / HSTS preload requirements not met

ID: networking/hsts-preload-misconfigured

Also available as: JSON · Markdown
88%Fix Rate
89%Confidence
55Evidence
2023-01-01First Seen

Version Compatibility

VersionStatusIntroducedDeprecatedNotes
any active

Root Cause

The HTTP Strict-Transport-Security (HSTS) header is absent or does not meet browser preload requirements. Without HSTS, browsers will accept HTTP connections before the first HTTPS redirect, leaving the first request vulnerable to downgrade attacks. HSTS preload additionally requires max-age >= 31536000, includeSubDomains, and the preload directive.

generic

Workarounds

  1. 92% success Add Strict-Transport-Security header at the web server (TLS termination) layer with progressive max-age
    1. Start with a short max-age to test: Strict-Transport-Security: max-age=300
    2. Verify your HTTPS setup works completely for 1-2 weeks
    3. Increase progressively: 86400 (1 day) → 604800 (1 week) → 2592000 (1 month) → 31536000 (1 year)
    4. nginx: add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
    5. Apache: Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
    6. Caddy: header Strict-Transport-Security "max-age=31536000; includeSubDomains"
    7. Verify: curl -I https://yourdomain.com | grep -i strict

    Sources: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security

  2. 85% success Submit to HSTS preload list only after all subdomains are HTTPS-ready
    1. Audit all subdomains: for sub in www api staging mail; do curl -I https://$sub.yourdomain.com 2>&1 | head -3; done
    2. Ensure ALL subdomains have valid HTTPS certificates (or decommission HTTP-only ones)
    3. Set the full preload header: Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
    4. Validate at https://hstspreload.org/ (checks all requirements automatically)
    5. Submit via https://hstspreload.org/ once the checker shows all green
    6. Note: removal requests take 6-12 months to propagate—be certain before submitting

    Sources: https://hstspreload.org/

  3. 88% success Use Helmet.js (Node.js) or SecurityMiddleware to set HSTS automatically
    1. Install helmet: npm install helmet
    2. Add to Express: app.use(helmet.hsts({ maxAge: 31536000, includeSubDomains: true }))
    3. For Django: SECURE_HSTS_SECONDS = 31536000; SECURE_HSTS_INCLUDE_SUBDOMAINS = True in settings.py
    4. For Rails: config.force_ssl = true in production.rb (sets HSTS automatically)
    5. Ensure the middleware runs on every request, including static file serving
    6. Test by checking the response headers in browser DevTools → Network → Headers

    Sources: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Strict-Transport-Security

Dead Ends

Common approaches that don't work:

  1. Submit a domain to the HSTS preload list before verifying all subdomains support HTTPS 85% fail

    HSTS preload with includeSubDomains forces all subdomains to HTTPS permanently in browsers. If any subdomain (staging, dev, mail, ftp) does not have a valid HTTPS certificate, users on those subdomains will receive certificate errors with no bypass option. Removal from the preload list takes months to propagate to all browser versions.

  2. Set max-age to a very large value (10 years or more) immediately on first HSTS deployment 70% fail

    Setting an extremely long max-age on the first deployment means if HTTPS breaks (certificate expires, server misconfiguration), browsers will refuse to load the site over HTTP for the full duration stored in their cache. Start with a short max-age, verify everything works, then progressively increase to the preload minimum of 31536000 (1 year).

  3. Add HSTS header only to the application layer (e.g., Express.js, Django) without the reverse proxy 60% fail

    When HTTPS termination happens at the reverse proxy (nginx, Caddy, load balancer), the application receives HTTP internally and may not always add the HSTS header to every response. The header must be set at the TLS-terminating layer to be reliably sent on all HTTPS responses, including static files and redirected responses.

Error Chain

Leads to:
Preceded by:
Frequently confused with: