HTTP Strict Transport Security (HSTS) tells browsers to stop retrying a site over plain HTTP after a successful secure visit. Once a browser learns that policy, it upgrades future requests to HTTPS automatically and blocks certificate bypass clicks that would reopen downgrade or SSL-stripping risk.
In Nginx, HSTS is sent with the Strict-Transport-Security response header from the HTTPS virtual host. The max-age value controls how long the browser keeps the policy, and the always parameter on add_header keeps the header on error responses as well as successful ones so the policy does not disappear on 404 or 500 pages.
Enable HSTS only after the site already has a trusted certificate and a stable HTTP-to-HTTPS redirect path, because a broken TLS configuration can lock users out until the cached policy expires. If a nested location or if in location block defines its own add_header directives, verify that the HSTS header still appears there as well instead of assuming the server-level header will be inherited automatically.
Related: How to improve Nginx security
Related: How to redirect HTTP to HTTPS in Nginx
$ curl -sSI https://example.com/ HTTP/2 200 server: nginx/1.28.3 date: Thu, 09 Apr 2026 13:37:38 GMT content-type: text/html content-length: 10671 last-modified: Wed, 08 Apr 2026 04:12:52 GMT
Do not enable HSTS on a hostname that still shows certificate warnings, mismatched names, or an incomplete certificate chain.
$ curl -sSI http://example.com/ HTTP/1.1 301 Moved Permanently Server: nginx/1.28.3 Date: Thu, 09 Apr 2026 13:37:38 GMT Content-Type: text/html Content-Length: 169 Connection: keep-alive Location: https://example.com/
Skip this check only when port 80 is intentionally closed for the hostname and readers are not expected to begin on HTTP.
$ sudoedit /etc/nginx/sites-available/example.com
Site files are commonly stored under /etc/nginx/sites-available with symlinks in /etc/nginx/sites-enabled, or directly under /etc/nginx/conf.d.
server {
listen 443 ssl;
server_name example.com www.example.com;
add_header Strict-Transport-Security "max-age=31536000" always;
##### snipped #####
}
For a first rollout, start with a short value such as max-age=300, verify behavior, then increase it to your intended long-lived policy.
Repeat the header in every HTTPS server block that serves the hostname or alias, such as example.com and www.example.com.
Add includeSubDomains or preload only when every current and future subdomain is ready for HTTPS, because browsers can keep enforcing that policy until max-age expires.
To remove an already deployed HSTS policy, serve Strict-Transport-Security: max-age=0 over HTTPS. Simply deleting the header does not clear a browser policy that is still cached.
Under the standard Nginx inheritance model, a lower level that defines its own add_header directives can change which headers are sent for that request path.
$ sudo nginx -t nginx: the configuration file /etc/nginx/nginx.conf syntax is ok nginx: configuration file /etc/nginx/nginx.conf test is successful
Related: How to test Nginx configuration
$ sudo systemctl reload nginx
Use sudo nginx -s reload on systems where systemd is not managing the service.
Related: How to manage the Nginx service
$ sudo systemctl is-active nginx active
If the unit reports failed or inactive, inspect the service log or journal before retrying the reload.
Related: How to manage the Nginx service
$ curl -sSI https://example.com/ | grep -i '^strict-transport-security:' Strict-Transport-Security: max-age=31536000
The header name is case-insensitive, but using the canonical Strict-Transport-Security spelling makes configuration reviews easier.
$ curl -sSI https://example.com/does-not-exist | grep -i '^strict-transport-security:' Strict-Transport-Security: max-age=31536000
If this check returns nothing, the header is usually missing the always parameter or being overridden in a more specific block.