My Site Was Hacked in 1 Second: HTTPS Enforcement and HSTS
1. "I Though Getting a Lock Icon Was Enough"
I bought an HTTPS certificate (SSL). I wrote Listen 443 in my Nginx config.
A green lock icon appeared in the browser address bar.
"Ah, now our site is safe! Our customers' precious data is secure."
I slept soundly. But hackers don't sleep.
I saw a shocking demo at a security seminar.
It took a hacker just 1 second to intercept login credentials for my site on a public WiFi (like Starbucks).
Even though I clearly applied HTTPS.
The culprit was the "First moment accessing via http://".
2. The Trap of Redirects: The 0.1-Second Gap
Users don't type https://naver.com in the address bar carefully.
They just type naver.com because they are lazy.
The browser then defaults to trying http://naver.com. (Modern Chrome tries HTTPS first sometimes, but it's not perfect yet.)
The server naturally responds:
"Oh? You came via HTTP? Please come back via HTTPS for security." (301 Moved Permanently)
The user's browser goes "Ah, okay!" and reconnects to https://naver.com.
This happens in 0.1 seconds, so we don't notice.
But hackers target this 0.1-second gap. This is called SSL Stripping attack.
The moment the user knocks on the door with http://, the hacker intercepts at the router level and says "I am the server" and sends a fake response.
The user is still communicating via http://, but the screen looks exactly like the login page. If they enter ID and PW? Sent straight to the hacker.
3. HSTS: Tattooing the Browser
To solve this, we must make the browser never even attempt to connect via http from the start.
That is HSTS (HTTP Strict Transport Security).
HSTS is a strong warning letter the server sends to the browser.
"Hey, listen. From now on, when you come to my site, come ONLY via HTTPS, absolutely. Even if the user types http:// by mistake, YOU change it to https:// automatically before coming. Got it?"
Receiving this warning, the browser writes in its notepad (HSTS Cache):
naver.com = HTTPS Only
Now, even if the user types http://naver.com, the browser changes it to https:// internally (Internal Redirect) before even sending a request to the server.
Since it changes before hitting the network, the hacker has no gap to intervene.
4. Dissecting the HSTS Header
Applying HSTS is easier than you think. Just add one line to the response header.
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
Let's break it down. If you don't understand the meaning, you could cause a major outage.
4.1. max-age=31536000
"Remember this rule for 1 year (31,536,000 seconds)."
The browser will never connect via HTTP during this period.
Usually set to 1 year (31536000) or 2 years (63072000).
4.2. includeSubDomains
"This applies to all my children (subdomains) too."
Applies to api.naver.com, blog.naver.com, test.naver.com, etc.
⚠️ Warning: Is your internal test server (dev.internal.naver.com) configured for HTTPS?
The moment you turn this on, that test server becomes inaccessible. The browser forces HTTPS connection, but the server doesn't accept HTTPS, resulting in "Connection Refused".
You must verify that all subdomains support HTTPS before enabling this.
4.3. preload
"Carve my name into the source code of browsers worldwide."
This is a scary option. I explain below.
5. Preload List: The Permanent Roster
HSTS has one weakness. "The First Contact".
To receive the warning letter (HSTS header), the browser has to connect at least once, right?
That first encounter can still be HTTP, and you can be attacked then.
So the Google (Chrome) team created the "HSTS Preload List".
This is a list hardcoded inside the Chrome browser installation files.
"Sites listed here (facebook.com, naver.com, toss.im, etc.) are HTTPS from birth. Don't question it."
If you register your site here, even if a user visits your site for the very first time in their life, the browser connects via HTTPS. It blocks even "The Risk of First Contact".
How to Register for Preload
- Add
preload option to header.
- Go to hstspreload.org and enter your domain.
- If conditions are met, it goes into the queue and is reflected in Chrome/Firefox/Safari updates a few weeks later.
☠️ Warning: Don't Cross the River of No Return
Be cautious, extremely cautious with Preload registration.
What if you registered by mistake and later need to use HTTP for some reason? (e.g., Legacy system integration)
You can't. Absolutely not.
Even if you apply for removal from the Preload list, it takes months for browsers worldwide to update.
During that time, users will only see "Connection Failed" screens. It could bankrupt a company.
6. Real World: Application Guide
Nginx Configuration
The most common setup. Add it under ssl_certificate block.
server {
listen 443 ssl http2;
server_name mysite.com;
ssl_certificate /etc/letsencrypt/live/mysite.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mysite.com/privkey.pem;
# This is the core! Send header 'always'.
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
}
Express (Node.js) Configuration
For Node.js backend, using helmet middleware is the easiest and safest way.
const express = require('express');
const helmet = require('helmet');
const app = express();
// Set up HSTS only, or use helmet defaults
app.use(helmet.hsts({
maxAge: 31536000, // 1 year
includeSubDomains: true, // Include subdomains
preload: true // Ready for preload submission
}));
Local Development (Localhost)
"Do I need HTTPS for local dev?"
Yes, it's better. You often need it for httpOnly cookies or SameSite settings.
However, localhost is often excluded from HSTS for developer convenience.
Instead, use mkcert to issue a local certificate, and map a .dev domain (e.g., myapp.dev) in /etc/hosts. .dev domains are on the Preload List by default, so HSTS works!
7. Step-by-Step Strategy (Guide for the Timid)
It's trouble if you blindly slap max-age=1 year and your site goes down.
I applied it gradually like this:
- Step 1 (Testing Waters):
max-age=300 (5 minutes). If something breaks, I only get blamed for 5 minutes. "Ah, server maintenance briefly."
- Step 2 (Confidence):
max-age=86400 (1 day). Watch traffic and logs for a day. Check if subdomains error out.
- Step 3 (Deployment):
max-age=31536000 (1 year). No turning back now.
- Step 4 (Wedge): Add
preload option and apply for Google registration. Now your site is officially recognized globally as "HTTPS Only".
7.95. Deep Dive: TLS 1.3 and The Future Protocol QUIC (HTTP/3)
The myth that "HTTPS is slow" is officially dead. TLS 1.3 changed the game.
- 0-RTT (Zero Round Trip Time): In the old days (TLS 1.2), starting an encrypted connection required a 4-step "Handshake" ("Hello? Take my key. Give me yours. Verified."). TLS 1.3 cut this down to 1 step, and for reconnection, 0 steps.
- QUIC (HTTP/3): We are even ditching TCP for UDP-based QUIC. It solves the "Head-of-Line Blocking" issue where one lost packet delays the entire stream. Google and YouTube are already streaming video to you using this.
Security is no longer a bottleneck for speed.
8. Conclusion: Security is Not Trusting Users
The first rule of security is "Do Not Trust Users".
Trusting "Users will figure out to come via HTTPS" is negligence.
Users are lazy; they won't type http://, they just type naver.com.
HSTS is the strongest Seatbelt a server can provide.
Check your site headers right now in the DevTools Network tab.
If Strict-Transport-Security is missing, your door is half open.
Lock it tight before a hacker squeezes through that gap.
7.995. FAQ
Q: Does HSTS hurt SEO?
A: No, it helps. Google boosts HTTPS sites. The 301 redirect also correctly transfers "Link Juice" to your HTTPS version.
Q: What happens if my certificate expires with HSTS on?
A: Total Blackout. Users cannot bypass the warning. They are locked out. Automate your renewals (Certbot)!
Q: Can I use HTTP for just one subdomain?
A: Only if you disable includeSubDomains. But be warned: compromised HTTP cookies can sometimes be used to attack the main domain.