HSTS Preloading for CDN Domains: Permanent HTTPS Enforcement Guide

Last winter, one of our fintech customers called in a panic after a penetration test showed their login page loading over plain HTTP at an airport hotspot — even though they'd had HSTS enabled for months. The tester hadn't broken TLS; they'd just intercepted the very first request before the browser ever knew to upgrade it. That's exactly the gap HSTS preloading is designed to close, and if you're serving anything sensitive behind a CDN, that first unauthenticated request is still your biggest exposure.

Why Standard HSTS Still Leaves Your CDN Domain Exposed

When you send a standard Strict-Transport-Security header, you're relying on Trust On First Use. The browser has to see that header once over a valid HTTPS connection before it learns to never use HTTP again. I've seen this bite teams over and over — they deploy a perfect HSTS policy with a year-long max-age, test it in Chrome, see the redirect working, and assume they're covered. They aren't, not until every single user has visited at least once without being intercepted.

For CDN-hosted domains, this problem gets amplified. Your edge might be serving thousands of hostnames — customer subdomains, apex domains, wildcard origins — and each of those hostnames has its own HSTS state in the browser. An attacker doesn't need to target your main app; they can go after assets.example.com or www.example.com on a user's first visit and downgrade the connection before your edge ever gets to send a header. We ran into this in 2019 with a media client who'd secured app.example.com but forgot their legacy image domain wasn't sending HSTS at all. The CDN was correctly configured, but the policy was never advertised where it mattered most.

Preloading solves this by baking your domain directly into the browser's source code. Chrome, Firefox, Safari, and Edge all ship with a list compiled from hstspreload.org — if you're on that preload list, the browser enforces HTTPS before it even makes a DNS lookup. There's no first HTTP request to intercept. According to the HTTP Working Group Specifications for HSTS (RFC 6797), this is the only way to provide true HTTPS enforcement from connection zero, and for CDN operators handling financial or healthcare traffic, we've made it a baseline requirement, not an optional hardening step.

# What the browser sees without preloading vs. with preloading

# Without preloading - first visit is still HTTP
GET http://example.com/  ->  301 Redirect to https://example.com/
# ^ This initial HTTP request can be stripped by an attacker

# With preloading - browser never attempts HTTP
# Internal browser check: example.com is in preload list
GET https://example.com/  ->  Direct TLS handshake (no HTTP fallback)

Configuring Your CDN Edge for Preload Eligibility

Getting onto the preload list isn't just about adding a header; the header has to be exact or your submission will be rejected. I've had submissions bounce because an edge rule was stripping the preload token on one regional PoP but not others — the validator checks your apex and your www subdomain and fails closed. You need max-age of at least 31536000 seconds (one year), includeSubDomains, and the preload directive, all served over HTTPS with a valid certificate chain.

Where folks get tripped up on CDNs is header inheritance and caching. If you set HSTS at your origin but your CDN is configured to cache and serve a stale HTTP response or to not forward that header on cache HITs, the scanner won't see it consistently. You want the header added at the edge, for every response, unconditionally. And don't forget your certificate strategy ties into this — if you're rotating certs or using different edge certificates per hostname, make sure every hostname that will be covered by includeSubDomains can actually serve valid HTTPS. That planning directly connects to your choice of Wildcard vs SAN Certificates for CDN: Making the Right Choice, because a gap in certificate coverage will break preloaded subdomains irreversibly for users.

Here's the edge configuration we've standardized on after testing across Nginx, Cloudflare Workers, and Akamai edge rules. The key is to return the header on both apex and www, and to ensure it survives cache. This aligns with the current guidance in web.dev Articles on HSTS deployment, which recommends testing with a short max-age first before committing to the preload value.

# Nginx origin / edge header (or equivalent CDN edge rule)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

# Cloudflare Worker / Fastly VCL equivalent
# VCL snippet for Fastly
sub vcl_deliver {
  set resp.http.Strict-Transport-Security = "max-age=63072000; includeSubDomains; preload";
}

# Verify before submitting - test both apex and www
curl -s -D- https://example.com | grep -i strict
curl -s -D- https://www.example.com | grep -i strict

# Both must return: strict-transport-security: max-age=31536000; includeSubDomains; preload

Submission, Validation, and Why Removal Takes Months

Submitting is deceptively easy — you enter your domain at hstspreload.org and it runs a set of live checks. But approval doesn't mean instant protection. Browser vendors pull the list on their own release cycles, and it can take 2-3 months before your domain is active in stable Chrome and Firefox. I always tell clients to treat submission day as day zero of a 12-week rollout, not a switch flip. And once you're in, you're making a long-term commitment. You can't just remove the header if something breaks; browsers will continue to force HTTPS until you complete the removal process and wait for another full release cycle.

That permanence is why we audit everything before submitting. First, make sure every subdomain can serve HTTPS — including internal tools, staging hosts, and legacy domains you might've forgotten. With includeSubDomains set, dev.example.com or legacy-api.example.com that still requires HTTP will become unreachable in preloaded browsers. Second, coordinate your certificate renewal. A preloaded domain with an expired certificate isn't just showing a warning — users can't click through or bypass it easily, and you can't quickly fall back to HTTP. That's why we pair preloading with Certificate Lifecycle Automation: From Issuance to Renewal at CDN Scale so renewals are hands-off. Finally, enable OCSP Stapling for CDN: Eliminating Certificate Validation Delays at the edge; when every connection is forced to TLS, you don't want revocation checks adding round trips.

The most painful story I've seen was a SaaS company that preloaded their apex without realizing a partner's custom CNAME — client1.example.com — was still pointing to an HTTP-only origin they didn't control. It took them five months to get delisted, during which that client's users were completely blocked. Check your DNS inventory first, then submit.

Validation Stage Requirement Common CDN Failure Mode Time Impact
Header Scan max-age ≥ 31536000 + includeSubDomains + preload Edge cache strips header on HIT or www subdomain missing directive Instant rejection
HTTPS Reachability Apex + www serve valid cert over HTTPS www CNAME points to non-TLS origin or cert mismatch 1-2 days to fix and rescan
Browser List Inclusion Merged into Chromium preload list on GitHub Waiting for browser stable release train 6-12 weeks to 95% user coverage
Removal Request Remove preload token + submit removal form Browsers cache entry until next release; users don't update instantly 3-6 months for full propagation
Subdomain Coverage All subdomains must support HTTPS Forgotten internal hostname breaks after preload activation Permanent breakage until HTTPS enabled

Deploying Preload-Ready Headers Across Your Entire Edge Fleet

You've validated eligibility — now comes the part where most teams trip. It's not enough to slap a header on your origin. Every edge node, every PoP, and every cached response must serve the exact same HSTS header over HTTPS, otherwise you'll fail preload validation or worse, create inconsistent enforcement. We've seen customers pass hstspreload.org checks on their origin but fail because a single legacy edge config for assets.cdn.example.com was still serving max-age=31536000 without preload.

The canonical header you need is non-negotiable: max-age=63072000; includeSubDomains; preload. That's 63,072,000 seconds — two full years. Don't use 31536000 (one year) if you're aiming for preload; while it technically meets the minimum, Chrome's scanner now flags anything under two years as a warning and you'll get rejected on resubmission after policy updates. And yes, includeSubDomains and preload must be present, no trailing semicolon issues, no quotes around the value when checking via low-level tools. You also can't have that header on HTTP — only HTTPS responses, with an unconditional 301 redirect from HTTP to HTTPS on the same host.

Before you push fleet-wide, stage it. Start with a 5-minute max-age (max-age=300) without preload on a single canary PoP and watch your logs for mixed-content errors or legacy clients. We've found that bumping record sizes or changing handshake behavior during this window can mask real HSTS issues, so it's worth reviewing your TLS Record Size Optimization: Balancing Latency and Throughput at the Edge settings in parallel to keep latency data clean. Once you're clean for 48-72 hours, roll the full 2-year header everywhere and let it bake for at least a week before you even think about submitting to hstspreload.org.

# Nginx (origin or edge) - must be in the 443 server block only
server {
    listen 443 ssl http2;
    server_name cdn.example.com;
    
    # Preload-ready: 2 years, every subdomain, preload flag
    add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

    # Enforce HTTPS redirect on port 80 - no HSTS header here!
    # server { listen 80; return 301 https://$host$request_uri; }
}

# Cloudflare Worker / Fastly VCL equivalent
# VCL: set beresp.http.Strict-Transport-Security = "max-age=63072000; includeSubDomains; preload";

# Verify from 3+ edge locations - don't trust just one PoP
curl -s -D - https://cdn.example.com -o /dev/null | grep -i strict
curl -s -D - https://assets.cdn.example.com -o /dev/null | grep -i strict
# Check eligibility instantly: https://hstspreload.org/?domain=cdn.example.com

Advanced Patterns: Subdomain Isolation, Certificate Coverage, and Surviving a Bad Preload

Here's the brutal reality of includeSubDomains: it means all subdomains. If you have legacy-http.partner.cdn.example.com that still serves images over plain HTTP for an old enterprise client, you can't preload cdn.example.com. Period. You'll brick that subdomain for any modern browser, and there's no wildcard exception. The pattern we recommend for large CDNs is domain isolation. Keep your preload candidate on a dedicated apex like cdn-secure.example.com where you can guarantee 100% HTTPS, and leave your mixed bag of legacy subdomains on a separate, non-preloaded domain. It’s less elegant, but it’s the only way to get permanent enforcement without a 3 a.m. incident.

Certificate coverage is the other silent killer. Preload doesn't check your cert once and forget it — browsers will hard-fail if any subdomain presents an invalid or mismatched certificate after you're preloaded, because they won't allow an HTTP fallback to bypass the error. That means your SANs or wildcard (*.cdn.example.com) must cover every single host that’s ever been DNS-resolved, including those dev and staging subdomains someone spun up three years ago. This is also where modern trust verification matters more than ever. If you're still thinking about pinning, read up on Beyond Certificate Pinning: Modern Alternatives for CDN Trust Verification — static pins and HSTS preload don't mix well, and CT monitoring plus automated ACME rotation is far safer at scale.

And if you do need to back out? Don't count on it being fast. Removal from the preload list is a manual request via the hstspreload.org removal form, then you wait for each browser vendor to ship a new list. Chrome pushes every 6-8 weeks, Firefox and Safari can take 10-14 weeks to sync, and that's after your removal is approved. We've seen domains stuck in enforced HTTPS for 4-6 months after they requested removal. That's why we tell teams to monitor handshake anomalies during rollout — subtle client failures can look like network blips. For that, TLS Fingerprinting Defense: Preventing Client Identification Through Handshake Analysis techniques actually help you spot which clients are failing closed due to HSTS versus normal packet loss.

Frequently Asked Questions

Can I preload just www.cdn.example.com without preloading the apex domain?

Yes, but you shouldn't rely on it. You can submit www.cdn.example.com separately, and it will be preloaded independently. However, if a user types cdn.example.com (without www) and you haven't preloaded the apex, that initial request is still vulnerable to SSL stripping on public Wi-Fi. For CDN domains, always preload the apex and use includeSubDomains — it’s the only way to close the gap. If you need www-only, you’ll need to ensure the apex 301s to www and is also served over HTTPS with its own HSTS header.

My preload submission shows "Does not includeSubDomains" even though my header does. What's wrong?

This almost always means inconsistent edge responses. The scanner at hstspreload.org checks your domain from multiple points and follows redirects. If even one redirect chain serves HTTP first, or one PoP returns a header without includeSubDomains due to cache variance, you’ll fail. Purge your entire edge cache, check with curl -L -v http://cdn.example.com to see the full redirect, and ensure your origin isn't stripping the header on cache HITs. We’ve seen Fastly and Akamai configs where beresp.http.Strict-Transport-Security was set only on MISS, not HIT.

How long after submission until browsers actually enforce my domain?

Submission is instant, enforcement is not. Once you pass validation, your domain is queued for the next preload list update. Chrome, which maintains the canonical list that Firefox, Safari, and Edge pull from, ships updates every 6-8 weeks with stable releases. Expect 8-12 weeks before 95% of global users have the updated list. You’ll see Chrome Canary and Beta pick it up in 2-3 weeks, but don't consider yourself protected until you've verified via chrome://net-internals/#hsts on a fresh Chrome Stable install.

Will HSTS preload break my Let's Encrypt HTTP-01 challenges for subdomains?

It can if you're not careful. HTTP-01 requires an initial HTTP request to /.well-known/acme-challenge/, but with preload, the browser (and increasingly, ACME clients that respect HSTS) will internally upgrade that to HTTPS before it hits the network. That’s fine if your edge correctly serves the challenge over HTTPS, but if your challenge handler only listens on port 80, validation will fail. Switch to DNS-01 or TLS-ALPN-01 for any preloaded domain — DNS-01 is the most reliable for wildcard and CDN subdomain coverage anyway.

Related Articles

References & Standards: HTTP Working Group Specifications · web.dev Articles · IETF Datatracker