Headers

Nginx HSTS max-age — Best Practices and Common Mistakes

Updated April 2026

Reading this? Verify your fix live. Check your HSTS config → HeadersFixer

HSTS max-age is the most consequential number in your HTTP headers. Set it too high before you're ready and users are locked out if HTTPS breaks. Set it too low and it provides no protection between visits. Here is the right progression.

The correct Nginx HSTS config

server { listen 80; server_name yourapp.com www.yourapp.com; # Redirect HTTP to HTTPS — do NOT add HSTS here return 301 https://yourapp.com$request_uri;
}

server { listen 443 ssl http2; server_name yourapp.com; # HSTS goes only in the HTTPS block add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
}

The ramp-up progression

Stagemax-age valueDurationWhat to verify
1. Testing86400 (1 day)1 weekHTTPS works, cert renews, no broken subdomains
2. Stable2592000 (30 days)2 weeksNo HTTPS issues, no complaints
3. Production31536000 (1 year)PermanentAll good — this is your target
4. Preload31536000 + preload flagSubmit to listAll subdomains HTTPS, includeSubDomains set

Common mistakes

Mistake 1 — HSTS in the HTTP block

# Wrong — browsers ignore HSTS over HTTP
server { listen 80; add_header Strict-Transport-Security "max-age=31536000"; # ← ignored return 301 https://...;
}

Mistake 2 — max-age=0 thinking it disables HSTS

# max-age=0 tells the browser to DELETE its stored HSTS record
# This is the correct way to remove HSTS — but you must send it over HTTPS
# Sending max-age=0 over HTTP does nothing

Strict-Transport-Security: max-age=0  # sent over HTTPS = removes HSTS # sent over HTTP = ignored

Mistake 3 — includeSubDomains before all subdomains are ready

# includeSubDomains applies HSTS to ALL subdomains
# If staging.yourapp.com is HTTP-only, users cannot access it after HSTS

# Check all subdomains support HTTPS before adding includeSubDomains:
# dev.yourapp.com, staging.yourapp.com, api.yourapp.com, etc.

Check if you're on the preload list

# hstspreload.org — check your domain status
# Requirements to be added:
# 1. Valid HTTPS certificate
# 2. All HTTP redirects to HTTPS
# 3. max-age >= 31536000 (1 year)
# 4. includeSubDomains present
# 5. preload present in the header

# Once on the preload list:
# HTTPS is enforced even on first visit (before browser has seen your header)
# This is PERMANENT — removal takes months to propagate from browsers

Verify your HSTS header

curl -I https://yourapp.com | grep -i strict-transport
# Should return:
# strict-transport-security: max-age=31536000; includeSubDomains; preload

Use HeadersFixer to scan your live site — it checks HSTS is present, sent over HTTPS only, and has a sufficient max-age value.

Check your HSTS config → HeadersFixer
Check if your domain is on the HSTS preload list → HSTS Preload Checker