HSTS is a security header that tells browsers to never connect to your site over HTTP again, even if the user types http:// or clicks an old HTTP link.
Let’s see it in action. Imagine you have a web server and you’ve just configured HSTS.
server {
listen 443 ssl;
server_name example.com;
ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload";
location / {
proxy_pass http://backend_app;
}
}
Here, max-age=31536000 means the browser should remember to use HTTPS for one year (31,536,000 seconds). includeSubDomains applies this rule to all subdomains (like blog.example.com). preload is a signal to HSTS preload lists, which can make your site even more secure by having browsers ship with a list of sites that must use HTTPS.
The core problem HSTS solves is the "SSL stripping" attack. An attacker on the same network could intercept your HTTP traffic and downgrade HTTPS connections to HTTP, allowing them to read or modify your data. By forcing browsers to only use HTTPS, HSTS effectively eliminates this attack vector.
Internally, when a browser first visits your HTTPS-enabled site and receives the Strict-Transport-Security header, it stores this directive. The next time you try to visit that domain, the browser will first attempt an HTTPS connection before it even sends the HTTP request. If the server doesn’t respond over HTTPS (e.g., it’s misconfigured or down), the browser will refuse to connect entirely, showing an error instead of a potentially insecure HTTP page.
The max-age directive is critical. A common mistake is setting it too low, like max-age=3600 (1 hour). This means the browser will only enforce HTTPS for an hour, leaving a significant window for downgrade attacks. For production, you want a long duration, typically max-age=31536000 (one year) or even max-age=63072000 (two years).
The includeSubDomains flag is also powerful but requires careful consideration. If you have any subdomains that don’t yet support HTTPS, enabling this flag will prevent users from accessing them. Ensure all your subdomains are correctly configured for HTTPS before adding this.
The preload directive is the most aggressive. When included, you submit your domain to a public list maintained by Google and other browser vendors. Browsers that use this list will have your domain hardcoded to always use HTTPS, even on the very first visit, before they’ve ever seen your HSTS header. This provides the strongest protection but also means you must have HTTPS working perfectly and reliably for your entire domain and all its subdomains, as there’s no way to easily remove yourself from the preload list once added.
When configuring HSTS, you might encounter issues if your SSL certificate is invalid or your server isn’t listening on port 443. A common diagnostic step is to use curl to inspect the headers. For instance, after deploying your HSTS configuration, you can run:
curl -I https://example.com
This command fetches only the headers (-I) for your HTTPS site. You should look for the Strict-Transport-Security header in the output. If it’s missing or has an incorrect value, your web server configuration needs adjustment.
The primary lever you control is the Strict-Transport-Security header itself. Its parameters (max-age, includeSubDomains, preload) are the knobs to turn. The surprising part is how easy it is to lock yourself out of your own site if you’re not careful with includeSubDomains and preload during the initial rollout, especially if you have legacy HTTP-only services or subdomains.
The next step after implementing HSTS is often managing certificate expirations to ensure continuous HTTPS availability.