Moving a site from HTTP to HTTPS sounds easy until you do it on a real production system with old links, mixed content, proxies, CDNs, and a few mystery services nobody wants to touch. Then HSTS enters the picture and raises the stakes, because once browsers cache that policy, mistakes stop being easy to undo.
That does not mean HSTS is risky in a bad way. I’d still recommend it for most public sites. But I would not enable it blindly on day one with a one-year max-age and preload unless I was very sure the whole stack was clean.
Here’s the practical comparison: how to migrate, what you gain, what can go wrong, and how to roll it out without locking users into a broken setup.
What changes when you move to HTTPS
Plain HTTP gives attackers too many opportunities: passive snooping, active content injection, cookie theft, and downgrade tricks. HTTPS fixes transport confidentiality and integrity, assuming your TLS setup is sane.
HSTS, via the Strict-Transport-Security header, adds another layer: it tells browsers to stop using HTTP for your domain after the first secure visit. That blocks protocol downgrade attacks and removes the “first try HTTP, then redirect” pattern for returning users.
A typical header looks like this:
Strict-Transport-Security: max-age=31536000; includeSubDomains
And if you are going all in:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
The preload token is not magic by itself. It is just a signal that your domain is intended for browser preload lists, and that comes with strict requirements. Treat preload as the final stage, not the starting point.
Quick comparison: HTTPS redirect only vs HTTPS + HSTS
HTTPS with redirects only
Pros
- Easy to roll out
- Easy to back out
- Good first migration step
- Works fine for many internal or lower-risk sites
Cons
- First request may still hit HTTP
- Users can be exposed to downgrade or SSL stripping on hostile networks
- Browsers keep trying HTTP unless links and bookmarks are already updated
HTTPS with HSTS
Pros
- Stronger downgrade protection
- Browsers automatically switch to HTTPS for repeat visits
- Helps enforce secure cookie and session handling habits
- Required if you want browser preload protection
Cons
- Misconfiguration hurts more
- Bad certificates become user-visible outages
includeSubDomainscan break forgotten legacy hosts- Preload is hard to reverse quickly
That is the real tradeoff: HSTS gives stronger guarantees, but it reduces your room for operational mistakes.
A sane migration plan
I like a phased rollout. It is boring, and boring is good in production.
Phase 1: Make HTTPS fully work before forcing it
Before you enable HSTS, get the site clean over HTTPS:
- Valid certificate chain
- No mixed content
- No HTTP-only asset references in templates, CSS, or JS
- Cookies marked
Securewhere appropriate - Redirect HTTP to HTTPS consistently
- APIs, static assets, images, fonts, and third-party scripts all loading over HTTPS
If your page still pulls http:// assets, browsers will block some of them and quietly degrade others. Fix that first.
A basic redirect in Nginx:
server {
listen 80;
listen [::]:80;
server_name example.com www.example.com;
return 301 https://$host$request_uri;
}
And your TLS server:
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name example.com www.example.com;
ssl_certificate /etc/ssl/certs/fullchain.pem;
ssl_certificate_key /etc/ssl/private/privkey.pem;
root /var/www/html;
index index.html;
}
Apache version:
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
Redirect permanent / https://example.com/
</VirtualHost>
<VirtualHost *:443>
ServerName example.com
ServerAlias www.example.com
SSLEngine on
SSLCertificateFile /path/to/fullchain.pem
SSLCertificateKeyFile /path/to/privkey.pem
</VirtualHost>
Phase 2: Add a short HSTS policy
Start with a low max-age. Something like 5 minutes to a day is reasonable during validation.
Nginx:
add_header Strict-Transport-Security "max-age=300" always;
Apache:
Header always set Strict-Transport-Security "max-age=300"
The always part matters. Without it, some responses may miss the header, especially error responses. That makes rollout less predictable.
At this stage, test everything:
- Main site
- Login flow
- Logout flow
- Static assets
- Error pages
- Redirect responses
- Mobile app webviews if you have them
- Alternate hostnames like
www,m,cdn,static,api
You can use browser dev tools and a scanner like HeaderTest to confirm the header is present where you expect it.
Phase 3: Increase max-age gradually
Once the site is stable:
- Move to
max-age=86400(1 day) - Then
max-age=2592000(30 days) - Then
max-age=31536000(1 year)
This phased approach is less exciting than flipping the “secure” switch immediately, but it gives you a way to catch edge cases before browsers cache your policy for months.
The biggest migration decision: includeSubDomains or not
This is where people get burned.
Strict-Transport-Security: max-age=31536000; includeSubDomains
includeSubDomains means every subdomain must be HTTPS-capable and stay that way. Not just the obvious ones. All of them.
Pros of includeSubDomains
- Stronger protection across the whole domain tree
- Prevents weak HTTP-only subdomains from becoming attack paths
- Usually required for preload readiness
Cons of includeSubDomains
- Breaks forgotten hosts like
old-admin.example.com - Can impact internal tools if they share the public parent domain
- Forces you to inventory infrastructure you may not fully control
My opinion: use includeSubDomains only after you have a real subdomain inventory. If your org has years of DNS cruft, this is not optional.
Preload: powerful, but unforgiving
Preload puts your domain into browser-maintained HSTS lists so even the first visit is HTTPS-only. That is the strongest version of this setup.
Pros of preload
- Protects first-time visitors
- Eliminates initial HTTP exposure
- Great for high-value public domains
Cons of preload
- Hard to undo quickly
- Requires long-term commitment to HTTPS everywhere
- Operational mistakes can take a while to clear from browsers
- Usually requires
max-age >= 31536000,includeSubDomains, and a redirecting HTTP endpoint
I only recommend preload when:
- Every public subdomain is known
- Every one of them supports HTTPS correctly
- You are confident this will remain true
If that sounds like a governance problem, that is because it is.
Common breakage during migration
Mixed content
You moved the page to HTTPS, but CSS, JS, images, fonts, or XHR still use http://.
Fix hardcoded URLs, or better, use root-relative or explicit HTTPS URLs.
Bad:
<script src="http://static.example.com/app.js"></script>
Good:
<script src="https://static.example.com/app.js"></script>
Secure cookies missing
Session cookies sent over HTTP are a gift to attackers.
Use Secure and usually HttpOnly:
Set-Cookie: session=abc123; Path=/; Secure; HttpOnly; SameSite=Lax
Proxy/CDN confusion
Your app thinks requests are HTTP because TLS terminates upstream, so it generates insecure redirects or wrong absolute URLs.
Make sure your framework trusts the right forwarding headers and that your proxy sets them correctly.
Partial HSTS deployment
You set HSTS on the homepage, but not on redirects, errors, or app responses behind another service. Browsers cache based on what they receive. Inconsistent delivery means inconsistent protection.
How to back out if something goes wrong
You can reduce HSTS by sending:
Strict-Transport-Security: max-age=0
That tells browsers to delete the cached policy. But there are two catches:
- Users must successfully reach your site over HTTPS to receive that header.
- If you preloaded the domain, browser preload lists are a separate process and not an instant rollback.
That is why I keep saying not to rush preload.
My recommended rollout
For most developer teams running a public site:
- Fix HTTPS everywhere
- Redirect all HTTP to HTTPS
- Set secure cookies
- Start HSTS with
max-age=300 - Increase to 1 day, then 30 days
- Add
includeSubDomainsonly after a real audit - Move to 1 year
- Consider preload only if you truly control the whole domain
If you want the shortest opinionated version: HTTPS redirects are table stakes. HSTS is the upgrade. Preload is the commitment ceremony.
That is the comparison nobody says out loud. The security benefit increases at each step, but so does the cost of being wrong.
For implementation details on server configuration and HSTS behavior, the official docs are worth checking:
And before you extend max-age or enable includeSubDomains, run a quick verification with HeaderTest. It is faster than discovering a broken subdomain after browsers have already cached your policy.