An SSL certificate gives your site the ability to serve HTTPS. HSTS — HTTP Strict Transport Security — makes HTTPS the only option. Once a browser receives your HSTS header, it remembers to upgrade every future request to your domain to HTTPS automatically, before the request leaves the device. No redirect needed. No opportunity for an attacker to intercept the initial HTTP hop.
Defined in RFC 6797 and supported by all major browsers since 2012, HSTS is a single response header with three directives. This guide explains each directive, shows exactly how to configure HSTS on Nginx, Apache, and IIS, and walks through the HSTS preload list — including why joining it is a one-way decision.
Prerequisite: a working SSL certificate
HSTS only works over HTTPS. Before enabling it, confirm your SSL certificate is correctly installed and your site loads cleanly over HTTPS with no mixed-content warnings. If you haven't installed SSL yet, see our guides for Nginx, Apache, or IIS 10.
What is HSTS?
HTTP Strict Transport Security is a browser security policy delivered as an HTTP response header. When your server sends:
Strict-Transport-Security: max-age=63072000; includeSubDomains; preload…the browser records the policy in its internal HSTS store. For the next 63,072,000 seconds (two years), any time a user visits your domain — even if they type http:// — the browser silently rewrites the request to https:// before sending it. The HTTP request never actually goes out.
Without HSTS, the flow when a user types your domain looks like this:
| Scenario | What happens on the first hop | Attack window? |
|---|---|---|
| SSL only, no HSTS | Browser sends plain HTTP request → server redirects to HTTPS | Yes — the HTTP hop is visible on the network |
| SSL + HSTS (returning visitor) | Browser rewrites to HTTPS internally — no HTTP request is sent | No |
| SSL + HSTS preload (first-ever visit) | Browser consults built-in list, sends HTTPS immediately | No |
HSTS removes the window between a user typing your URL and your server's redirect arriving. On an untrusted network — public Wi-Fi, a compromised router — that window is where SSL-stripping attacks live. (If you haven't set up that server-side redirect yet, do it first — our HTTP-to-HTTPS redirect guide has the exact config for Apache, Nginx, and IIS.)
How HSTS works
HSTS relies on a simple browser-side cache:
First HTTPS visit
The user visits your site over HTTPS (either directly or after your 301 redirect from HTTP). Your server responds with the Strict-Transport-Security header.
Browser stores the policy
The browser saves the domain, max-age expiry time, and whether includeSubDomains applies. This entry lives in the browser's HSTS database.
All future requests are upgraded
Every subsequent request to your domain — whether from a link, a typed URL, or a redirect — is internally rewritten to HTTPS by the browser before any data is sent.
Timer refreshes on each visit
Each time a browser receives the HSTS header, the max-age clock resets. Regular visitors effectively stay protected indefinitely. Infrequent visitors whose policy expires fall back to one unprotected HTTP hop.
The first-visit problem (TOFU)
HSTS is a "Trust On First Use" (TOFU) mechanism. The very first HTTP request to your domain — before the browser has ever seen your HSTS header — still goes out unprotected. On most networks this is fine; a café network could theoretically intercept it.
The HSTS preload list solves this. Browsers ship with a hardcoded list of domains that must use HTTPS from the very first connection. If your domain is on it, there is no unprotected first hop.
The Strict-Transport-Security header explained
The full header has one required directive and two optional ones:
Strict-Transport-Security: max-age=<seconds>[; includeSubDomains][; preload]max-age=<seconds> Required
How long (in seconds) the browser should remember to enforce HTTPS. Each HTTPS response refreshes the timer from the moment it is received.
| Value | Duration | Use for |
|---|---|---|
| 300 | 5 minutes | Initial testing |
| 86400 | 1 day | Extended testing / staging |
| 31536000 | 1 year | Production (preload minimum) |
| 63072000 | 2 years | Production (preload recommended) |
includeSubDomains Optional (required for preload)
Extends the HSTS policy to every subdomain of the current host. If you set HSTS on example.com with includeSubDomains, browsers will also enforce HTTPS on api.example.com, mail.example.com, and every other subdomain — even ones you haven't thought about. Audit all subdomains before enabling this. See pitfalls.
preload Optional
Signals that you consent to having your domain included in browser preload lists. The directive alone does nothing — you must also separately submit your domain to the preload list. But without it in the header, your submission will be rejected. Think of it as "I give permission to hardcode this domain in browsers."
Only send HSTS over HTTPS
RFC 6797 specifies that browsers must ignore the Strict-Transport-Security header when received over HTTP. Sending it on your HTTP virtual host is harmless from a security standpoint (browsers ignore it), but it's a configuration mistake that can cause confusion. Place the directive only in your HTTPS server block or HTTPS virtual host.
Enable HSTS on Nginx
Add add_header inside your server {} block for port 443. The always parameter ensures the header is sent on every response code — 200, 301, 404, 500 — not just 2xx responses.
Phase 1: test with a short max-age
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/ssl/certs/example.com_fullchain.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
# Start here: 5-minute policy for testing
add_header Strict-Transport-Security "max-age=300" always;
# ... rest of config ...
}Visit your site, check the header is present (see Verifying your config), and confirm all pages load correctly over HTTPS.
Phase 2: production config
server {
listen 443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/ssl/certs/example.com_fullchain.crt;
ssl_certificate_key /etc/ssl/private/example.com.key;
# Production: 2-year policy, ready for preload submission
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
}
# HTTP → HTTPS redirect (no HSTS header here)
server {
listen 80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}After editing, test the Nginx config and reload:
sudo nginx -t && sudo systemctl reload nginxAlready have Nginx SSL installed? See the full Nginx SSL installation guide for OCSP Stapling, HTTP/2, and cipher configuration.
Enable HSTS on Apache
Apache uses the mod_headers module for custom response headers. Verify it's enabled first:
sudo a2enmod headers
sudo systemctl restart apache2Phase 1: test config
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/example.com_fullchain.crt
SSLCertificateKeyFile /etc/ssl/private/example.com.key
# Phase 1: 5-minute policy for testing
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=300"
</IfModule>
</VirtualHost>Phase 2: production config
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
SSLEngine on
SSLCertificateFile /etc/ssl/certs/example.com_fullchain.crt
SSLCertificateKeyFile /etc/ssl/private/example.com.key
# Production: 2-year policy, preload-ready
<IfModule mod_headers.c>
Header always set Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
</IfModule>
</VirtualHost>
# HTTP → HTTPS redirect (no HSTS here)
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
Redirect permanent / https://example.com/
</VirtualHost>Test and reload:
sudo apachectl configtest && sudo systemctl reload apache2Rocky Linux / AlmaLinux note: The service is called httpd, not apache2. Use sudo systemctl reload httpd. The mod_headers module is included by default but may need enabling in /etc/httpd/conf.modules.d/. See the Apache SSL installation guide for the full virtual host structure.
Enable HSTS on IIS
IIS 10.0.17763 (Windows Server 2019) and later include native HSTS support via the site bindings dialog. Earlier IIS 10 versions and IIS 8/8.5 require a web.config custom header.
Option A: IIS Manager (Windows Server 2019/2022)
Open IIS Manager and select your site.
In the Actions pane, click Bindings.
Select your HTTPS binding and click Edit.
Click Advanced Settings… at the bottom of the dialog.
Check "Enable HTTP Strict Transport Security (HSTS)".
Set max-age (e.g., 63072000) and optionally check includeSubDomains and preload.
Click OK and restart the site.
Option B: web.config (all IIS versions)
Add the header to your HTTPS site's web.config. Do not add it to the HTTP site used for redirects.
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<httpProtocol>
<customHeaders>
<add name="Strict-Transport-Security"
value="max-age=63072000; includeSubDomains; preload" />
</customHeaders>
</httpProtocol>
</system.webServer>
</configuration>For a complete IIS SSL setup including URL Rewrite redirects and SNI binding, see the IIS 10 SSL installation guide.
The HSTS preload list
The HSTS preload list is a database of domains that browsers embed directly in their source code. Chrome, Firefox, Safari, and Edge all import it, which means listed domains get HTTPS-enforced from the very first connection — solving HSTS's first-visit limitation entirely.
As of early 2026, around 120,000 domains are on the Chrome preload list. Fewer than 36% of sites that send an HSTS header include the preload directive, so most HSTS-protected sites still have the first-visit gap.
Requirements for preload submission
All five requirements must be met before submitting:
max-age ≥ 31536000 (1 year)
The preload list recommends 63072000 (2 years). Shorter values will be rejected.
includeSubDomains is present
The preload policy covers all subdomains. You cannot preload just the apex domain.
preload is present
Without this directive, the hstspreload.org tool will reject your submission.
HTTP → HTTPS redirect on the apex domain
http://example.com must redirect to https://example.com (not to https://www.example.com without a second redirect).
All subdomains work over HTTPS
Including www. if a DNS record exists for it. Any subdomain that doesn't support HTTPS will become inaccessible to browsers that preloaded your policy.
How to submit
Once your header meets all requirements, visit hstspreload.org and enter your domain. The tool checks your live HSTS header against the requirements and queues a submission to the Chrome preload list. Timeline:
- Submission → Chrome Canary: days
- Chrome Canary → Chrome stable: weeks (one Chrome release cycle)
- Chrome → Firefox, Safari, Edge: weeks to months (each imports on their own schedule)
Preload is effectively a one-way door
Removal is technically possible via hstspreload.org, but it takes months to propagate through browser releases and user updates. Many users will still have the preloaded policy a year after removal. Only submit if you are committed to maintaining HTTPS on your domain — including all subdomains — indefinitely. If there's any chance you'll need to revert to HTTP or change your subdomain structure, wait.
Pitfalls and gotchas
Subdomain audit before includeSubDomains
Run a DNS enumeration of your domain before enabling includeSubDomains. Tools like dig or online DNS lookup services can surface forgotten subdomains — staging., dev., api., mail., cdn., etc. Every subdomain you find must be reachable over HTTPS, or it will become unreachable for browsers that cached your HSTS policy.
Expiring certificates become hard errors under HSTS
Normally, browsers show an expired-certificate warning with an optional 'proceed anyway' bypass. HSTS removes that bypass. Visitors with a cached HSTS policy cannot reach your site at all if the certificate expires. This makes certificate monitoring even more critical once HSTS is active. See our guide on SSL certificate expiration and outages.
Redirect chains break HSTS
If your HTTP site redirects to another HTTP URL before eventually landing on HTTPS — for example, http://example.com → http://www.example.com → https://www.example.com — the first hop is still unprotected. Fix it so http://example.com redirects directly to https://example.com in one step.
Browser cache makes testing awkward
Once a browser caches your HSTS policy, it ignores the header value you send until the cached policy expires. When testing, use a fresh browser profile or clear the HSTS cache via chrome://net-internals/#hsts (Chrome) or about:networking#hsts (Firefox). Start with max-age=300 so the test cache clears in 5 minutes.
localhost is exempt
Browsers treat localhost as a special secure origin that's exempt from HSTS. You won't accidentally lock yourself out of local development. However, if you use custom local domains (e.g., myapp.local via /etc/hosts), HSTS does apply to those, so test carefully.
Verifying your HSTS config
1. Command line (curl)
The quickest check is a curl HEAD request — it bypasses any browser HSTS cache and shows you exactly what the server is sending:
curl -sI https://yourdomain.com | grep -i strict-transport-securityExpected output:
strict-transport-security: max-age=63072000; includeSubDomains; preload2. Browser DevTools
Open DevTools → Network tab → click the document request → Response Headers. Look for strict-transport-security. Note: if the browser already has the domain in its HSTS cache, it may show an internal redirect and not hit the server at all — use curl to get the raw server response.
3. Chrome's HSTS internals
In Chrome, navigate to chrome://net-internals/#hsts. You can look up, delete, and manually add HSTS policies for specific domains — invaluable for testing without waiting for max-age to expire.
4. Preload eligibility check
The hstspreload.org tool checks whether your current header meets all five preload requirements and shows exactly which ones are failing. Use it before submitting.
Ready to add HSTS to your site?
You need a valid SSL certificate installed before HSTS does anything. Browse our DV SSL options — from $3.99/year, issued in minutes.