Skip to content
← All articles

HSTS and HSTS Preload: Complete Guide to Forced HTTPS

HSTS and HSTS Preload: Complete Guide to Forced HTTPS

HTTP Strict Transport Security (HSTS) is a security mechanism that tells the browser: “for this domain, always use SSL/TLS проверку, even if the user typed http://”. Preload goes further: the domain ships inside browsers before the first visit. Correctly configured HSTS protects against MITM on the first connection; wrong config can lock your site offline for months. Here's how to do it safely.

How HSTS works

Server returns the Strict-Transport-Security header on HTTPS responses:

Strict-Transport-Security: max-age=31536000; includeSubDomains; preload

Directives:

After receiving the header, the browser caches the rule. The next request to http://example.com is rewritten internally to https:// without hitting the network. This prevents SSL-stripping: a Wi-Fi attacker can't downgrade HTTPS to HTTP.

First-visit limitation (TOFU)

HSTS is Trust-On-First-Use — the rule enters the browser only after a first HTTPS visit. If the user's first request is plain HTTP, the attacker can proxy it and stay on HTTP. Fix — preload list: your domain is baked into Chrome/Firefox/Safari/Edge source code, and HSTS applies before any visit.

Step-by-step HSTS rollout

Step 1: make sure everything is HTTPS

All subdomains, CDNs, API документацию, and images must work on HTTPS. Eliminate mixed content — see mixed content fix.

Step 2: short max-age for testing

# nginx
add_header Strict-Transport-Security "max-age=300" always;

300 seconds (5 min) is long enough to check the site works. If something breaks, the rule expires in 5 minutes. Test with DevTools and enterno.io Security Scanner.

Step 3: raise max-age gradually

add_header Strict-Transport-Security "max-age=86400" always;   # 1 day
# one week later
add_header Strict-Transport-Security "max-age=2592000" always; # 1 month
# one month later
add_header Strict-Transport-Security "max-age=31536000" always; # 1 year

Step 4: add includeSubDomains

Only when you're sure every subdomain serves HTTPS. Otherwise users lose access to blog.example.com if it's still HTTP.

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

Step 5: submit to preload list

After a month with long max-age and includeSubDomains — submit to hstspreload.org.

Preload list requirements

  1. Valid cert on apex and all subdomains.
  2. HTTP redirects to HTTPS with 301.
  3. HTTPS on apex serves the HSTS header.
  4. Header includes all three directives: max-age=31536000; includeSubDomains; preload.
  5. max-age of at least 1 year.

Pre-check: curl -sI https://example.com | grep -i strict-transport. After approval you'll appear in Chrome in 6-12 weeks, other browsers later.

HTTP → HTTPS redirect in nginx

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$host$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    # ... SSL config ...

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

Important: the header must have the always flag or nginx won't attach it to 4xx/5xx responses (and the preload validator will reject it).

Pitfalls

Cannot roll back quickly

Once HSTS is on with a long max-age, you can't turn off HTTPS — every user who cached the rule will fail if HTTPS breaks. Fix: set max-age=0 and wait for all caches to expire (up to a year). Preload list removal takes 6+ months; freshly added domains are removed faster.

Every subdomain must work

includeSubDomains applies to EVERY subdomain, including forgotten ones. Check via Certificate Transparency:

curl "https://crt.sh/?q=%25.example.com&output=json" | jq '.[].name_value' | sort -u

All issued certs — gives you the subdomain list. Each must serve HTTPS or it breaks for users.

Dev/staging environments

If you have dev.example.com on HTTP — includeSubDomains kills it. Fix: move dev to a separate domain (dev.example-dev.com) or enable HTTPS everywhere.

HSTS + Let's Encrypt

Problem: if Let's Encrypt renewal breaks and the cert expires, HSTS prevents users from bypassing the warning. This stops MITM but amplifies the damage of a real error (see expired cert). Fix — robust monitoring: Enterno.io Monitors with alerts at 14/7/3 days before expiry, plus SMS escalation for critical services.

Config verification

Frequently asked questions

Do I need HSTS if everything is already HTTPS?

Yes. HSTS protects against the first HTTP request. Without preload, the first visit is still vulnerable to SSL-stripping.

How do I remove a domain from the preload list?

File a removal request at hstspreload.org. Processing takes 3-12 months. Recently added domains are faster; long-lived preload entries take longer.

Does HSTS cover subdomains without their own header?

With includeSubDomains — yes, the rule is inherited. But each subdomain must still serve a valid SSL cert.

Can I have HSTS without HTTP→HTTPS redirect?

Technically yes, but preload requires a redirect. Without it, a user typing example.com gets an HTTP failure (if no HTTP server) or an HTTP page (if there is), defeating the purpose.

Conclusion

HSTS with preload is table stakes for a modern HTTPS site. Setup is simple but requires discipline: start with a short max-age, test, add includeSubDomains, then submit for preload. Check readiness via Security Scanner, guard against regressions via Monitors. Related: mixed content and weak cipher suites.

HSTS — RFC 6797. Preload — hstspreload.org. OWASP HSTS Cheat Sheet — cheatsheetseries.owasp.org.

Check your website right now

Check now →
More articles: SSL
SSL
Expired SSL Certificate: How to Fix NET::ERR_CERT_DATE_INVALID
15.04.2026 · 8 views
SSL
Self-Signed Certificates: When to Use Them and How to Avoid Warnings
15.04.2026 · 6 views
SSL
TLS 1.3 vs TLS 1.2: What Changed and How to Migrate Correctly
15.04.2026 · 8 views
SSL
SSL Handshake Failed: Root Causes and Step-by-Step Diagnosis
15.04.2026 · 7 views