Learn / Security / Lesson 01
HTTPS and HSTS
Why having HTTPS is not enough, how one header stops a whole class of attacks, and why that header is hard to take back.
Last updated: 2026-09-19
What it is
HTTPS encrypts the connection between a browser and a server. HSTS (HTTP Strict Transport Security) is a response header that tells the browser: for the next N seconds, never talk to this site over plain HTTP, not even once.
Strict-Transport-Security: max-age=31536000After a browser has seen that header, typing schema-ph.dev, clicking an old http:// link, or following a bookmark all go straight to HTTPS inside the browser. The insecure request is never sent.
Why it is a rule
A site can have a perfect certificate and still be attacked, because the first request often is not HTTPS. A person types a bare domain, the browser tries http://, and the server answers with a redirect to https://. That first plain-text hop is the gap.
- sslstrip (2009). Moxie Marlinspike showed at Black Hat DC that an attacker on the same network can sit in that first hop, quietly keep the victim on HTTP, and talk HTTPS to the real site on their behalf. The victim sees a normal page with no padlock, and almost nobody notices a missing padlock.
- Firesheep (2010). A free Firefox add-on that let anyone on café Wi-Fi click on other people's Facebook and Twitter sessions and become them. It worked because those sites sent the session cookie over plain HTTP after login. It pushed the big sites to HTTPS everywhere.
HSTS was standardised in 2012 (RFC 6797) as the answer to sslstrip: once the browser knows the rule, there is no plain-text hop left to hijack.
The Philippines has a lot of shared and public Wi-Fi, and many networks run TLS-intercepting proxies. The first-visit gap is not theoretical here.
How to do it
- Serve every page over HTTPS and redirect HTTP to HTTPS.
- Send
Strict-Transport-Securityon every HTTPS response in production. - Start with a short
max-age(a day) while you check nothing breaks, then raise it to a year (31536000). - Add
includeSubDomainsonly once every subdomain serves HTTPS. - Add
preloadand submit to the browser preload list only when you are sure it is permanent.
if (process.env.NODE_ENV === 'production') {
app.use((req, res, next) => {
res.setHeader('Strict-Transport-Security', 'max-age=31536000')
next()
})
}Do not send it in development. On localhost it pins your dev server to HTTPS inside your own browser until you clear the entry by hand.
How we do it here
Every production response from schema-ph.dev carries HSTS with a one-year max-age. Our subdomains are not included yet, and the site is not on the preload list; both wait until every subdomain is confirmed HTTPS-only.
Benefits
- Closes the first-request downgrade gap for every returning visitor.
- Stops cookies ever travelling in plain text to this host.
- Browsers refuse to let a user click through a certificate warning on an HSTS site, so a fake certificate cannot be waved past.
- One header, no code paths, no runtime cost.
Disadvantages
- It is sticky. Each visitor's browser remembers it for the full
max-age. If you ever need plain HTTP again, you cannot switch it off for those people. - The very first visit is still exposed. HSTS only works after the browser has seen it once. Only the preload list covers the first visit.
includeSubDomainscan break things. An old admin panel or a mail host on HTTP stops loading entirely.- Preload is slow to undo. Getting off the list takes months to reach every browser.
Checklist
- Every production HTTPS response sends
Strict-Transport-Security. max-ageis at least one year once stable.includeSubDomainsonly when every subdomain is HTTPS.- No HSTS on localhost.
Sources
- RFC 6797 — HTTP Strict Transport Security
- MDN — Strict-Transport-Security
- hstspreload.org
- Moxie Marlinspike, New Tricks for Defeating SSL in Practice, Black Hat DC 2009.
- Eric Butler, Firesheep, ToorCon 2010.