Imagine you walk into a bank that has two entrances: an unlocked door with no guard (HTTP) and a reinforced bulletproof vault entrance (HTTPS).
Out of habit, people often walk toward the regular unlocked door. When you step inside, the teller spots you and says: “Hey, please move over to the secure bulletproof entrance!” (HTTP 301/302 Redirect).
What if an attacker is standing right in the open doorway before you make it inside? That is the fundamental vulnerability of standard HTTPS redirection — and HSTS exists to seal that dangerous doorway permanently.
The Problem: Why HTTPS Redirects Aren’t Enough#
Most users never type https:// in the browser URL bar. They simply type bank.com or click on legacy links.
By default:
- The browser makes the first request over plain, unencrypted HTTP (port 80).
- The server replies with a
301 Moved Permanentlyredirecting tohttps://...(port 443). - The browser establishes the secure HTTPS connection.
That tiny gap in Step 1 creates an opportunity for attackers to execute a SSL Stripping (or SSL Downgrade) attack.
sequenceDiagram
autonumber
actor User as 👤 User
participant Attacker as 🦹 Attacker (MitM / Public WiFi)
participant Server as 🏢 Web Server
User->>Attacker: 1. Visit http://example.com (Plain unencrypted HTTP)
Note over Attacker: Intercept initial HTTP request
Attacker->>Server: 2. Establish legitimate HTTPS connection
Server-->>Attacker: 3. Return encrypted web content
Attacker-->>User: 4. Strip SSL & send back plain HTTP version
Note over User,Attacker: Passwords, session cookies & tokens stolen!
[!WARNING] On public Wi-Fi networks (cafes, airports), a malicious actor can intercept the plaintext HTTP request, establish HTTPS with the real server, and serve you unencrypted HTTP. Your browser won’t show any SSL error warnings because it is legitimately speaking plain HTTP!
The Solution: What is HTTP Strict Transport Security (HSTS)?#
HSTS (HTTP Strict Transport Security) is a security response header sent by the web server to the browser, declaring:
“From now on, regardless of whether the user types
http://or clicks an HTTP link, ALWAYS upgrade tohttps://locally (Internal Redirect 307) BEFORE sending any packet onto the network!”
When HSTS is enabled:
- The browser transforms every HTTP request into HTTPS internally on the client side.
- No unencrypted HTTP traffic ever leaves the machine.
- If the SSL certificate is invalid, untrusted, or expired, the browser blocks access completely without offering an “Accept risk and continue” button — eliminating Man-in-the-Middle bypasses.
sequenceDiagram
autonumber
actor User as 👤 User
participant Browser as 🌐 Browser (HSTS Enabled)
participant Attacker as 🦹 Attacker (MitM)
participant Server as 🏢 Web Server
User->>Browser: Types "http://example.com"
Note over Browser: Check HSTS Cache: HTTPS strictly enforced!
Browser->>Browser: Internal redirect to "https://example.com" (307)
Browser->>Server: Direct secure HTTPS connection (End-to-End Encrypted)
Note over Attacker: Completely blinded! Unable to intercept or decrypt traffic.
Server-->>Browser: Safe encrypted response
Anatomy of the HSTS Header#
The header syntax is concise and declarative:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preloadhttpDirective breakdown:
| Directive | Description | Example |
|---|---|---|
max-age=<seconds> | How long (in seconds) the browser should cache and enforce this policy. | max-age=31536000 (1 year) |
includeSubDomains | (Optional) Extends HSTS policy to all subdomains (e.g., api.domain.com, blog.domain.com). | Required for preload eligibility |
preload | (Optional) Consents to hardcoding your domain into global browser binaries. | Prevents Trust-on-First-Use vulnerability |
HSTS Preloading: Closing the “First Visit” Gap (TOFU)#
Even with HSTS active, there is one remaining vulnerability known as Trust on First Use (TOFU):
- If a user visits your website for the first time ever on a fresh computer, their browser hasn’t seen your HSTS header yet.
- That very first connection still travels over unencrypted HTTP.
How HSTS Preload Works#
To eliminate this initial gap, major browser vendors (Chrome, Firefox, Safari, Edge) maintain a shared HSTS Preload List:
- You configure the HSTS header with all required flags.
- You submit your domain to hstspreload.org ↗.
- Once verified, your domain is hardcoded into the browser source code.
- Even on a brand new device, the browser knows to connect over HTTPS immediately from millisecond zero!
graph TD
A[User types: example.com] --> B{In Browser HSTS Preload List?}
B -->|Yes - Hardcoded in Browser| C[Direct HTTPS Connection Immediately]
B -->|No| D{In Browser HSTS Local Cache?}
D -->|Yes - Visited before| C
D -->|No - First time visit| E[Send initial HTTP Request]
E --> F[Server responds with HTTPS + HSTS Header]
F --> G[Cached locally for future visits]
[!TIP] Top-Level Domains (TLDs) such as
.dev,.app, and.pagehave HSTS preloading enabled at the registry level by default.
How to Configure HSTS on Web Servers#
1. Nginx#
Add the header inside the HTTPS server block (port 443):
server {
listen 443 ssl http2;
server_name example.com www.example.com;
# SSL certificates...
# Enable HSTS (1 year, subdomains, preload)
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
}nginx2. Apache#
Ensure the headers module is enabled (a2enmod headers):
<VirtualHost *:443>
ServerName example.com
Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
</VirtualHost>apache3. Caddy Server#
Caddy manages SSL certificates automatically. Add HSTS via the header directive:
example.com {
header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
reverse_proxy localhost:3000
}plaintext4. Cloudflare / Edge CDN#
If using Cloudflare:
- Open Cloudflare Dashboard -> Select your domain.
- Navigate to SSL/TLS -> Edge Certificates.
- Scroll to HTTP Strict Transport Security (HSTS) -> Click Enable HSTS.
- Set
Max-Age, toggleInclude SubdomainsandPreload.
Important Migration Guidelines#
[!CAUTION] HSTS is irreversible in the short term! If you enable
includeSubDomainswithmax-age=1 yearand an internal subdomain (likeold-admin.domain.com) lacks a valid SSL certificate, users will be completely locked out for an entire year.
Follow a phased rollout strategy:
-
Phase 1 — Short test period:
httpStrict-Transport-Security: max-age=300(Test for 5 minutes to verify no broken services).
-
Phase 2 — Subdomain verification:
httpStrict-Transport-Security: max-age=86400; includeSubDomains(Run for 24 hours to ensure all subdomains support SSL).
-
Phase 3 — Long-term production & Preload:
httpStrict-Transport-Security: max-age=31536000; includeSubDomains; preload(Submit to hstspreload.org ↗ once validated).
Summary#
| Feature | Standard HTTP Redirect | With HSTS Enabled |
|---|---|---|
| First Request | Sent unencrypted over HTTP (Vulnerable) | Upgraded locally by browser to HTTPS |
| SSL Stripping Protection | ❌ None | ✅ Complete protection |
| SSL Error Handling | Allows user bypass (“Proceed anyway”) | Strict hard block (No bypass) |
| Latency | 1 extra round-trip (301 redirect) | Zero extra latency (Internal 307) |
Enabling HSTS is a lightweight configuration with massive security benefits. Protect your users and web infrastructure by enforcing it today.