How to Configure HSTS Headers in Nginx
In this tutorial, you'll learn about How to Configure HSTS Headers in Nginx. We cover key concepts, practical examples, and best practices.
The Problem
Your site serves over HTTPS, but a user can still connect via HTTP first (a redirect). This creates a window for man-in-the-middle attacks. The HSTS (HTTP Strict Transport Security) header tells browsers to always use HTTPS for your domain, eliminating the insecure redirect opportunity. Without HSTS, tools like SSLstrip can downgrade connections, and a determined attacker on the same network can intercept the initial HTTP request before the redirect occurs.
Quick Fix
1. Add the Strict-Transport-Security header
server {
listen 443 ssl;
server_name example.com;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
# SSL config
ssl_certificate /etc/ssl/certs/example.com.pem;
ssl_certificate_key /etc/ssl/private/example.com.key;
}
The max-age=31536000 is 1 year in seconds. includeSubDomains applies HSTS to all subdomains. The always flag ensures the header is sent even on error responses.
2. Test the HSTS header
curl -I https://example.com
Expected output:
HTTP/2 200
strict-transport-security: max-age=31536000; includeSubDomains
Use curl -I https:// (not http://) — HSTS headers are only sent over HTTPS.
3. Configure preload (submit to browser vendors)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
The preload directive tells browsers to hardcode your domain as HTTPS-only. You must also submit your domain to https://hstspreload.org.
Requirements for preload:
max-ageat least 31536000 (1 year)includeSubDomainsdirectivepreloaddirective- Valid HTTPS certificate on the main domain and all subdomains
- Redirect HTTP to HTTPS (301)
4. Redirect HTTP to HTTPS (required for HSTS)
server {
listen 80;
server_name example.com;
# Required: redirect all HTTP to HTTPS
return 301 https://$host$request_uri;
}
server {
listen 443 ssl;
server_name example.com;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
ssl_certificate /etc/ssl/certs/example.com.pem;
ssl_certificate_key /etc/ssl/private/example.com.key;
location / {
proxy_pass http://localhost:3000;
}
}
Browsers only respect HSTS when the response comes over HTTPS. The HTTP redirect must be a 301 (permanent) redirect.
5. Start with a short max-age for testing
add_header Strict-Transport-Security "max-age=300" always;
Use 5 minutes (max-age=300) during testing. If something breaks, the HSTS policy expires quickly. Once confirmed, change to 1 year.
6. Verify HSTS in Chrome
Open Chrome DevTools → Network tab → click a request → Headers tab. Look for strict-transport-security in the Response Headers section.
7. Check HSTS status in Chrome
Navigate to chrome://net-internals/#hsts and enter your domain in the "Query HSTS/PKP domain" section. It shows the HSTS policy active for your domain.
8. Remove a domain from Chrome's HSTS list
# Open Chrome and navigate to:
chrome://net-internals/#hsts
# Enter the domain and click "Delete domain security policies"
Prevention
- Always include
alwaysin the add_header directive - Use
includeSubDomainsonly if ALL subdomains support HTTPS - Start with
max-age=300for testing, then increase tomax-age=31536000 - Test with
curl -Ibefore enabling preload - Only enable preload when you're certain your site will always serve HTTPS
- Add a redirect from HTTP to HTTPS (301) in a separate server block
- Once preload is active, it can take months to remove — test thoroughly first
- Remove HSTS in Chrome via
chrome://net-internals/#hstsif needed during development
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro