Skip to content

How to Configure HSTS Headers in Nginx

DodaTech 2 min read

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-age at least 31536000 (1 year)
  • includeSubDomains directive
  • preload directive
  • 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 always in the add_header directive
  • Use includeSubDomains only if ALL subdomains support HTTPS
  • Start with max-age=300 for testing, then increase to max-age=31536000
  • Test with curl -I before 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/#hsts if needed during development

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro