0
0
NginxHow-ToBeginner · 3 min read

How to Configure HSTS in Nginx for Secure HTTPS

To configure HSTS in nginx, add the header Strict-Transport-Security inside your server block that listens on HTTPS. For example, use add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; to enforce HTTPS for one year including subdomains.
📐

Syntax

The Strict-Transport-Security header tells browsers to only use HTTPS for your site for a set time. It has these parts:

  • max-age: Time in seconds browsers remember to use HTTPS (e.g., 31536000 for 1 year).
  • includeSubDomains: Optional; applies rule to all subdomains.
  • preload: Optional; requests inclusion in browser preload lists.
  • always: Ensures header is sent on all responses, including errors.
nginx
add_header Strict-Transport-Security "max-age=SECONDS; includeSubDomains; preload" always;
💻

Example

This example shows how to enable HSTS in an HTTPS server block for one year, including subdomains and preload request.

nginx
server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;

    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    location / {
        root /var/www/html;
        index index.html;
    }
}
Output
When a browser visits https://example.com, the response header includes: Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
⚠️

Common Pitfalls

Common mistakes when configuring HSTS in nginx include:

  • Adding the header in the HTTP (port 80) block instead of HTTPS (port 443) block, so browsers never receive it securely.
  • Omitting always, causing the header to be missing on error pages.
  • Setting max-age too low or forgetting includeSubDomains when needed.
  • Enabling HSTS before HTTPS is fully working, which can lock users out.
nginx
server {
    listen 80;
    server_name example.com;

    # Wrong: HSTS header on HTTP block
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";

    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate /etc/ssl/certs/example.com.crt;
    ssl_certificate_key /etc/ssl/private/example.com.key;

    # Correct: HSTS header with 'always' to send on all responses
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;

    location / {
        root /var/www/html;
        index index.html;
    }
}
📊

Quick Reference

Remember these tips when configuring HSTS in nginx:

  • Always add the header in the HTTPS server block.
  • Use always to ensure header is sent on all responses.
  • Set max-age to at least 6 months (recommended 1 year).
  • Include includeSubDomains if you want all subdomains secured.
  • Use preload only if you plan to submit your site to browser preload lists.

Key Takeaways

Add the Strict-Transport-Security header only in the HTTPS server block.
Use the 'always' parameter to send HSTS header on all responses including errors.
Set max-age to a long duration like 31536000 seconds (1 year) for strong security.
Include 'includeSubDomains' to protect all subdomains under your domain.
Test HTTPS fully before enabling HSTS to avoid locking out users.