blog.dopana

Back

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:

  1. The browser makes the first request over plain, unencrypted HTTP (port 80).
  2. The server replies with a 301 Moved Permanently redirecting to https://... (port 443).
  3. 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 to https:// 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; preload
http

Directive breakdown:

DirectiveDescriptionExample
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:

  1. You configure the HSTS header with all required flags.
  2. You submit your domain to hstspreload.org.
  3. Once verified, your domain is hardcoded into the browser source code.
  4. 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 .page have 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):

nginx.conf
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;
}
nginx

2. 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>
apache

3. Caddy Server#

Caddy manages SSL certificates automatically. Add HSTS via the header directive:

Caddyfile
example.com {
    header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
    reverse_proxy localhost:3000
}
plaintext

4. Cloudflare / Edge CDN#

If using Cloudflare:

  1. Open Cloudflare Dashboard -> Select your domain.
  2. Navigate to SSL/TLS -> Edge Certificates.
  3. Scroll to HTTP Strict Transport Security (HSTS) -> Click Enable HSTS.
  4. Set Max-Age, toggle Include Subdomains and Preload.

Important Migration Guidelines#

[!CAUTION] HSTS is irreversible in the short term! If you enable includeSubDomains with max-age=1 year and an internal subdomain (like old-admin.domain.com) lacks a valid SSL certificate, users will be completely locked out for an entire year.

Follow a phased rollout strategy:

  1. Phase 1 — Short test period:

    Strict-Transport-Security: max-age=300
    http

    (Test for 5 minutes to verify no broken services).

  2. Phase 2 — Subdomain verification:

    Strict-Transport-Security: max-age=86400; includeSubDomains
    http

    (Run for 24 hours to ensure all subdomains support SSL).

  3. Phase 3 — Long-term production & Preload:

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

    (Submit to hstspreload.org once validated).

Summary#

FeatureStandard HTTP RedirectWith HSTS Enabled
First RequestSent unencrypted over HTTP (Vulnerable)Upgraded locally by browser to HTTPS
SSL Stripping Protection❌ None✅ Complete protection
SSL Error HandlingAllows user bypass (“Proceed anyway”)Strict hard block (No bypass)
Latency1 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.

References#