How to Enable HSTS on Nginx, Apache, and Cloudflare

HSTS tells browsers to always use HTTPS for your domain. This guide shows how to add the Strict-Transport-Security header safely across popular web servers and platforms.


HTTP Strict Transport Security (HSTS) is a security header that instructs browsers to always connect to your website over HTTPS, even if the user types http:// or clicks an HTTP link. Enabling HSTS prevents downgrade attacks and protocol confusion. This guide shows you how to enable it safely on the most common web servers and platforms.

Before Enabling HSTS: Checklist

HSTS is difficult to reverse once browsers have cached it. Before enabling, confirm:

  • Your website has a valid, trusted SSL certificate installed.
  • HTTPS works correctly on all subdomains (if using includeSubDomains).
  • HTTP traffic redirects to HTTPS (301 redirect).
  • You do not plan to move away from HTTPS — HSTS makes HTTP unavailable for the duration of max-age.
  • Your SSL certificate is set to auto-renew.
HSTS is hard to undoOnce a browser receives the HSTS header, it will refuse to load your site over HTTP for the duration of max-age. If your SSL certificate expires or is misconfigured, users will be completely blocked from your site. Only enable HSTS when your HTTPS setup is confirmed stable.

The HSTS Header

The HSTS header is Strict-Transport-Security. A complete header looks like:

http
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
DirectiveDescription
max-age=31536000How long (in seconds) browsers remember to use HTTPS. 31536000 = 1 year.
includeSubDomainsApply HSTS to all subdomains. Only add if all subdomains have valid HTTPS.
preloadRequest inclusion in browser HSTS preload lists. Only add when ready for full commitment.

Recommended Rollout: Start Small

Start with a short max-age and no includeSubDomains, then gradually increase:

http
# Stage 1: Test — 5 minutes Strict-Transport-Security: max-age=300 # Stage 2: Short term — 1 day Strict-Transport-Security: max-age=86400 # Stage 3: Medium term — 1 month Strict-Transport-Security: max-age=2592000 # Stage 4: Include subdomains — 6 months Strict-Transport-Security: max-age=15768000; includeSubDomains # Stage 5: Full — 1 year with preload Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

Enabling HSTS on Nginx

nginx
server { listen 443 ssl; server_name example.com www.example.com; ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem; ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem; # Add HSTS header add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always; # ... rest of your config } # Redirect HTTP to HTTPS server { listen 80; server_name example.com www.example.com; return 301 https://$host$request_uri; }

After editing, test and reload Nginx:

bash
sudo nginx -t && sudo systemctl reload nginx

Enabling HSTS on Apache

apache
<VirtualHost *:443> ServerName example.com SSLEngine on SSLCertificateFile /etc/ssl/fullchain.pem SSLCertificateKeyFile /etc/ssl/private.key # Enable HSTS Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" </VirtualHost> <VirtualHost *:80> ServerName example.com # Redirect HTTP to HTTPS Redirect permanent / https://example.com/ </VirtualHost>

Make sure mod_headers is enabled:

bash
sudo a2enmod headers sudo systemctl restart apache2

Enabling HSTS on Cloudflare

  1. Log in to the Cloudflare dashboard and select your domain.
  2. Go to SSL/TLSEdge Certificates.
  3. Scroll to HTTP Strict Transport Security (HSTS).
  4. Click Enable HSTS.
  5. Configure max-age, includeSubDomains, and preload as needed.
  6. Click Save.
Cloudflare HSTS and your originCloudflare adds the HSTS header at the edge. Your origin server does not need to set the header separately when using Cloudflare's proxy. However, if you bypass Cloudflare (grey cloud), the header will not be added.

Enabling HSTS in Next.js

javascript
// next.config.mjs const nextConfig = { async headers() { return [ { source: '/(.*)', headers: [ { key: 'Strict-Transport-Security', value: 'max-age=31536000; includeSubDomains; preload', }, ], }, ]; }, }; export default nextConfig;

Verifying HSTS Is Active

Check that the HSTS header is being sent correctly:

bash
# Check with curl curl -I https://example.com | grep -i "strict-transport" # Expected: strict-transport-security: max-age=31536000; includeSubDomains; preload # Check with openssl openssl s_client -connect example.com:443 -quiet | head -20

Or use the ShowDNS HSTS Checker to verify the header is present and correctly configured.

Frequently Asked Questions

Can I remove HSTS once set?

To remove HSTS, set max-age=0 in the Strict-Transport-Security header. This tells browsers to stop enforcing HSTS for your domain. However, users who already have the old HSTS cached will still enforce HTTPS until their cached value expires.

What happens if my SSL certificate expires while HSTS is active?

Users with HSTS cached will be unable to access your site. Their browser will refuse both the HTTP and HTTPS connections — HTTP because of HSTS, HTTPS because of the invalid certificate. This is why HSTS requires a robust SSL renewal process.

Should I add the preload directive immediately?

No. The preload directive requests inclusion in browser HSTS preload lists. Once included, removing the domain from these lists takes months. Only add preload when you are fully committed to HTTPS for your domain and all subdomains indefinitely.

Related Articles