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.
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.
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.
ID: site-health-check.security-posture.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-Security header value. Parse the max-age directive and convert it to an integer for comparison against the 31536000-second (1 year) threshold.
Detector snippet (shell-capable tools only): If curl is available, fetch HEAD against $BASE_URL and inspect the response headers for Strict-Transport-Security. If the header is present and max-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-Security header is present in the final response with a max-age value 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-age is 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