HSTS header is present
Why it matters
Without HSTS, browsers will happily downgrade to HTTP on any request where the redirect is absent or stripped — a trivial attack on public Wi-Fi. HSTS instructs browsers to refuse HTTP connections entirely for the specified duration, eliminating the window where a man-in-the-middle can intercept the initial plaintext request. NIST SP 800-53 SC-8 and OWASP A02 (Cryptographic Failures) both flag the absence of HSTS as a transport security gap. A max-age below one year (31536000 seconds) leaves users exposed for shorter periods after each visit — insufficient to meaningfully protect repeat visitors. CWE-311 captures the failure: missing encryption of sensitive data in transit.
Severity rationale
High because a missing or short-lived HSTS header leaves a downgrade window open on every first visit or after the max-age expires, enabling interception without any vulnerability in the application itself.
Remediation
Add the Strict-Transport-Security header with a max-age of at least one year. In nginx:
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
In Next.js next.config.js:
async headers() {
return [{
source: '/(.*)',
headers: [{ key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains' }]
}];
}
Do not set HSTS on HTTP — it is silently ignored and only takes effect over HTTPS. Confirm deployment with curl -sI https://yoursite.com | grep -i strict.
Detection
-
ID:
hsts-header -
Severity:
high -
What to look for: Before evaluating, enumerate all security-related headers in the final response and extract the exact
Strict-Transport-Securityheader value. Parse themax-agedirective and convert it to an integer for comparison against the 31536000-second (1 year) threshold. -
Detector snippet (shell-capable tools only): If
curlis available, fetch HEAD against$BASE_URLand inspect the response headers forStrict-Transport-Security. If the header is present andmax-age>=31536000, pass. If missing or exit >=2 / command not found, fall back to prose reasoning.curl -sS -I $BASE_URL/ -
Pass criteria:
Strict-Transport-Securityheader is present in the final response with amax-agevalue of at least 31536000 seconds (1 year). The header must appear in the actual HTTP response, not just in an HTML meta tag. -
Fail criteria: Header missing entirely, or
max-ageis present but the value is less than 31536000 seconds. -
Skip (N/A) when: The final URL uses
http://(HSTS is only meaningful over HTTPS connections). -
Detail on fail:
"No Strict-Transport-Security header in response"or"HSTS max-age is 3600 — should be at least 31536000 (1 year)" -
Remediation: HSTS tells browsers to always connect via HTTPS, preventing downgrade attacks. Configure it in your server or CDN:
# nginx add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;// next.config.js headers() { key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains' }Verify after deploying with:
curl -sI https://yoursite.com | grep -i strict
External references
- cwe · CWE-311 — Missing Encryption of Sensitive Data
- owasp:2021 · A02 — Cryptographic Failures
- nist:rev5 · SC-8 — Transmission Confidentiality and Integrity
Taxons
History
- 2026-04-18·v1.0.0·Initial import from site-health-check·automated
- 2026-04-20·v1.1.0·Add Phase 6.0 detect-curl snippet for deterministic HSTS header probe·by cakleinman