Suggest an editImprove this articleRefine the answer for “How HTTPS works and difference from HTTP”. Your changes go to moderation before they’re published.Approval requiredContentWhat you’re changing🇺🇸EN🇺🇦UAPreviewTitle (EN)Short answer (EN)**HTTPS** is HTTP over TLS. It encrypts all traffic between browser and server and verifies the server's identity through a certificate signed by a CA. ``` // HTTP port 80 - plaintext, readable by anyone on the network // HTTPS port 443 - TLS 1.3, AES-256-GCM encrypted ``` **Key:** use HTTPS for all production traffic; TLS 1.3 keeps handshake overhead under 50ms.Shown above the full answer for quick recall.Answer (EN)Image**HTTPS** is HTTP with a TLS layer on top. It encrypts every byte between browser and server and verifies the server's identity before any data flows. ## Theory ### TL;DR - HTTP is a postcard (anyone on the network reads it). HTTPS is a sealed envelope with an ID check - The main difference: TLS handshake negotiates encryption keys and validates the server certificate before data moves - HTTP uses port 80, HTTPS uses port 443 - TLS 1.3 cut the handshake to one round trip, so the performance gap is now negligible - If it touches users or data, use HTTPS. Only localhost is an exception ### Quick example ```javascript // Node.js: HTTP vs HTTPS server const express = require('express'); const https = require('https'); const http = require('http'); const fs = require('fs'); const app = express(); app.get('/data', (req, res) => res.json({ token: 'secret-abc123' })); // Port 8080: token visible in Wireshark as plaintext http.createServer(app).listen(8080); // Port 8443: token encrypted, Wireshark shows gibberish https.createServer({ key: fs.readFileSync('key.pem'), cert: fs.readFileSync('cert.pem') }, app).listen(8443); ``` On port 8080, every request is readable on the wire. Wireshark shows the token in plain JSON. On port 8443 with TLS, the same response is unreadable without the session keys. ### Key difference HTTP sends everything as plaintext. A proxy, a coffee shop router, or anyone between client and server can read passwords, session tokens, and API keys. HTTPS wraps all of that in TLS, which uses ECDHE to agree on a shared secret and then encrypts data with AES-256-GCM. The server also proves its identity via an X.509 certificate signed by a trusted CA, so the client knows it is talking to the real server and not an impostor. ### When to use - Login forms, payment flows, or any API that handles tokens: HTTPS, no exceptions - Public pages with no user data: HTTPS still, because Google ranks HTTP pages lower and HSTS prevents going back once set - Static assets (images, CSS, JS): HTTPS, because mixed content on an HTTPS page gets blocked by the browser - Internal tools: HTTPS with self-signed certs (browser shows a warning, but the habit of encrypted connections matters) - Local development on localhost: HTTP is fine; browsers treat localhost as a secure context ### Comparison table | Aspect | HTTP | HTTPS | |---|---|---| | Port | 80 | 443 | | Encryption | None (plaintext) | TLS 1.3, AES-256-GCM | | Server authentication | None | X.509 cert from a CA (e.g., Let's Encrypt) | | Attack surface | Eavesdropping, injection, MITM | Mitigated (cert pinning adds another layer) | | Performance | No handshake overhead | +10-50ms handshake; negligible with TLS 1.3 | | When to use | Local dev only | All production traffic | ### How the TLS handshake works The browser opens TCP to port 443 and sends a ClientHello with supported ciphers and a key share. The server replies with a ServerHello, its certificate chain, and a signed key share. The browser checks the certificate against its CA trust store (Chrome uses BoringSSL, Node.js uses OpenSSL) and computes a shared secret via ECDHE. From that point, all HTTP data travels encrypted with symmetric keys. TLS 1.3 does this in one round trip. TLS 1.2 needed two. That difference adds up on high-latency connections, which is why upgrading matters. I have seen legacy internal services skip TLS entirely because the network was "closed." Three weeks later, a compromised developer laptop was the inside network. HTTPS within the perimeter is not paranoia; it is cheap insurance. ### Common mistakes **Serving production APIs on HTTP.** Tokens and session cookies travel as plaintext. Wireshark on the same Wi-Fi captures them in under a second. The fix: ```javascript // Express: redirect HTTP to HTTPS app.set('trust proxy', 1); app.use((req, res, next) => { if (!req.secure) { return res.redirect(301, `https://${req.headers.host}${req.url}`); } next(); }); ``` **Ignoring certificate expiry.** Let's Encrypt certificates expire every 90 days. Browsers show a hard block, not a warning. Automate renewal: ```bash certbot renew --dry-run # Add to cron: 0 3 * * * certbot renew --quiet ``` **Using self-signed certs in production.** Browsers show `NET::ERR_CERT_AUTHORITY_INVALID` and most users leave immediately. Self-signed certs are fine for internal dev. Production needs a cert from a real CA. Let's Encrypt is free. **No HSTS header.** Without it, a user who types `example.com` hits HTTP first, giving an attacker a one-request window for a downgrade attack. The fix: ```javascript res.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload'); ``` **HTTP/2 without TLS.** Browsers require TLS for HTTP/2 per RFC 7540. Enabling HTTP/2 in Nginx without a valid certificate causes browsers to fall back to HTTP/1.1 silently. ### Real-world usage - Express: `https.createServer({ key, cert }, app)` for any API handling user data - Next.js: HSTS via `next.config.js` headers array with `max-age=63072000` - Nginx: `ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;` with Certbot for auto-renewal - Stripe: registers only HTTPS URLs for webhooks; HTTP endpoints are rejected at registration - AWS ALB: enforce HTTPS via a 301 redirect listener rule on port 80 ### Follow-up questions **Q:** What changed between TLS 1.2 and TLS 1.3? **A:** TLS 1.3 cut the handshake from 2 round trips to 1, removed RSA key exchange (which had no forward secrecy), and made AEAD ciphers like AES-GCM mandatory. Fewer negotiation options also means a smaller attack surface. **Q:** What is forward secrecy and why does it matter? **A:** ECDHE generates a fresh key pair per session. If an attacker records encrypted traffic today and later steals the server's private key, they still cannot decrypt old sessions. TLS 1.2 with RSA key exchange does not have this property. **Q:** How does certificate validation actually work? **A:** The browser checks the certificate's signature against the issuing CA's public key, walks up the chain to a root CA in its trust store, checks the expiry date, and queries OCSP or a CRL to confirm the cert was not revoked. **Q:** What is HSTS preloading? **A:** Browsers ship with a hardcoded list of domains from hstspreload.org that are forced to HTTPS before any request is made. Google requires `max-age` of at least one year plus `includeSubDomains` before accepting a submission. Removal from the list takes months and browser release cycles. Once preloaded, there is no fast rollback. **Q:** What is a mixed content error? **A:** An HTTPS page loading any resource over HTTP triggers mixed content. Scripts and iframes get blocked. Images show a warning. The fix is to use relative URLs or ensure every subresource is on HTTPS. ## Examples ### Basic: HTTP vs HTTPS server in Node.js ```javascript const express = require('express'); const https = require('https'); const http = require('http'); const fs = require('fs'); const app = express(); app.get('/profile', (req, res) => { res.json({ user: 'alice', token: 'secret-abc123' }); }); // HTTP: token visible in Wireshark as plaintext JSON http.createServer(app).listen(8080, () => { console.log('HTTP on 8080 - traffic is sniffable'); }); // HTTPS: token encrypted, Wireshark shows raw bytes https.createServer({ key: fs.readFileSync('key.pem'), cert: fs.readFileSync('cert.pem') }, app).listen(8443, () => { console.log('HTTPS on 8443 - encrypted'); }); ``` `curl http://localhost:8080/profile` returns the token in readable JSON. On a shared network, anyone can capture it with Wireshark. The HTTPS version returns the same data but encrypted end-to-end. ### Intermediate: HTTPS redirect and Stripe webhook ```javascript const express = require('express'); const https = require('https'); const http = require('http'); const fs = require('fs'); const stripe = require('stripe')('sk_test_...'); const app = express(); app.use(express.raw({ type: 'application/json' })); // Redirect all HTTP traffic to HTTPS http.createServer((req, res) => { res.writeHead(301, { Location: `https://${req.headers.host}${req.url}` }); res.end(); }).listen(80); // Stripe requires HTTPS for webhook endpoints app.post('/webhook', (req, res) => { const sig = req.headers['stripe-signature']; try { const event = stripe.webhooks.constructEvent(req.body, sig, 'whsec_...'); // handle event res.json({ received: true }); } catch (err) { res.status(400).send(`Webhook error: ${err.message}`); } }); https.createServer({ key: fs.readFileSync('privkey.pem'), cert: fs.readFileSync('fullchain.pem') }, app).listen(443); ``` Stripe validates `stripe-signature` on every webhook. Over HTTP, that signature can be stripped and replaced. HTTPS prevents that. Stripe also rejects HTTP URLs at registration time, so this is not optional. ### Advanced: HSTS and mixed content in the browser ```javascript // Express: set HSTS header on every response app.use((req, res, next) => { res.set('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload'); next(); }); // React: fetch works because HSTS blocks HTTP for preloaded domains async function fetchUserData(userId) { const response = await fetch(`https://api.example.com/users/${userId}`, { credentials: 'include', // cookies only sent over TLS referrerPolicy: 'strict-origin-when-cross-origin' }); if (!response.ok) throw new Error('Fetch failed'); return response.json(); } // Mixed content: blocked by Chrome on HTTPS pages const img = new Image(); img.src = 'http://insecure.com/photo.jpg'; // Chrome blocks this console.log('Blocked: mixed content policy'); ``` The HSTS header tells the browser to refuse HTTP for this origin for up to a year. The `preload` flag submits the domain to the list baked into Chrome. The blocked image appears in DevTools Network tab with status `(blocked:mixed-content)`.For the reviewerNote to the moderator (optional)Visible only to the moderator. Helps review go faster.