Home CS Fundamentals Missing HTTP Redirect — 12% of Login Credentials Exposed
Beginner 6 min · March 06, 2026
HTTP and HTTPS Explained

Missing HTTP Redirect — 12% of Login Credentials Exposed

12% of login requests over HTTP due to missing redirect — session hijacking spikes.

N
Naren Founder & Principal Engineer

20+ years shipping production systems from the metal up. Notes here come from systems that actually shipped.

Follow
Production
production tested
July 18, 2026
last updated
2,466
articles · all by Naren
Before you start⏱ 20 min
  • Basic programming fundamentals
  • A computer with internet access
  • Willingness to follow along with examples
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • HTTP is plaintext on port 80. Anyone on the path — coffee shop WiFi, ISP, corporate proxy, or nation-state — can read your users' passwords, tokens, and credit cards in cleartext.
  • HTTPS is HTTP inside TLS. It gives you encryption, server authentication, and integrity checks in one package.
  • The TLS handshake uses asymmetric crypto (public/private keys, usually ECDHE) to safely negotiate a session key, then flips to fast symmetric encryption — AES-GCM or ChaCha20 in practice.
  • Port 443 is default for HTTPS. Modern browsers shame HTTP pages with big "Not Secure" warnings, Google continues to downgrade them in search, and features like Service Workers, geolocation, camera access, and Private Access Tokens simply refuse to work on insecure contexts.
  • HTTP/2 and especially HTTP/3 (over QUIC) are HTTPS-only in practice. If you're not on TLS, you're stuck with 20-year-old performance characteristics and head-of-line blocking.
  • The biggest production foot-gun: the padlock only means the pipe is encrypted and the certificate validated for that domain. It says nothing about whether the site is legitimate, the backend is secure, or the code isn't leaking data.
✦ Definition~90s read
What is HTTP and HTTPS?

HTTP (Hypertext Transfer Protocol) is the foundation of data communication on the web — it's the protocol your browser uses to request a webpage and receive the HTML, images, and assets that render it. When you type a URL and hit Enter, your browser opens a TCP connection to the server, sends an HTTP request (typically a GET), and the server responds with a status code (like 200 OK) and the requested content.

Picture sliding a note across the table in a busy café.

The critical flaw is that HTTP transmits everything in plaintext — every byte of your request, including any form data or cookies, is sent over the network exactly as-is, readable by any machine between you and the server. This is why a missing redirect from HTTP to HTTPS is catastrophic: if your site serves a login form over HTTP, even if the form action points to HTTPS, the initial request and response are exposed, and an attacker on the same Wi-Fi network can intercept the credentials before the redirect ever happens — a 12% exposure rate in real-world scans means millions of credentials leak daily from sites that should know better.

HTTPS wraps HTTP inside TLS (Transport Layer Security), which encrypts the entire conversation. The TLS handshake establishes a shared secret using asymmetric cryptography (RSA or ECDHE), verifies the server's identity via a certificate signed by a trusted Certificate Authority (like Let's Encrypt, which issues 300+ million certificates free), and then switches to symmetric encryption (AES-GCM) for the actual data transfer.

This means even if an attacker intercepts the packets, they see only ciphertext — the login credentials, session cookies, and request bodies are invisible. The missing redirect problem occurs when a site listens on port 80 (HTTP) but doesn't issue a 301 or 302 redirect to port 443 (HTTPS), or when the redirect happens after the credentials are already sent in the initial HTTP request.

Modern best practice is HSTS (HTTP Strict Transport Security), which tells browsers to always use HTTPS for your domain, eliminating the redirect window entirely.

In the ecosystem, HTTP/HTTPS sits at the application layer of the TCP/IP stack, above TCP (transport) and IP (network). You'll use HTTP status codes daily: 200 (success), 301 (permanent redirect), 302 (temporary redirect), 401 (unauthorized), 403 (forbidden), 404 (not found), and 500 (server error).

Request methods are GET (retrieve), POST (submit data), PUT (replace), PATCH (partial update), and DELETE. Headers like Content-Type, Authorization, Set-Cookie, and Cache-Control control how data flows. When NOT to use HTTPS? Never — there's no valid reason in 2024.

Even static content sites should redirect HTTP to HTTPS; Let's Encrypt makes it free, and the performance overhead is negligible (TLS 1.3 handshake takes one round trip vs. HTTP's zero). The only exception is local development on localhost, where self-signed certificates suffice, but even then, tools like mkcert make HTTPS trivial.

Plain-English First

Picture sliding a note across the table in a busy café. With HTTP, it's written in plain ink — the waiter, the person at the next table, or anyone who snatches it mid-air can read your PIN, your secrets, everything. HTTPS is the same note, but now it's in a locked briefcase that only you and your friend have the key for. The briefcase still travels the same route through the café. The path hasn't changed. But the contents are now private, can't be altered without detection, and you have high confidence it's actually going to your friend, not an impostor pretending to be them. That's the entire game.

Every single time a user opens your site or calls your API, their browser and your server are having a very specific conversation using HTTP or HTTPS. Get this wrong and passwords leak, sessions get hijacked, Google buries you in search, and half your fancy new browser features stop working.

HTTP dates back to 1991. Tim Berners-Lee designed it for simplicity and speed when the web was mostly academic pages. Privacy wasn't even on the radar. Everything travels in plain text. Passwords, session cookies, API keys, credit card details — all completely readable by anyone with a packet sniffer on the same network path.

HTTPS is simply HTTP sent over TLS. The TLS layer adds the three things the original protocol never had: confidentiality through encryption, authentication through certificates, and integrity so tampering is detectable.

By April 2026 this isn't optional theater. Chrome, Firefox, and Safari actively mark HTTP pages as 'Not Secure' with increasingly aggressive UI. Google has been using HTTPS as a ranking signal for years and only gets stricter. Features like geolocation, camera access, service workers, and the newer Private Access Tokens flat-out refuse to work on insecure origins. The real failures I've seen in production aren't usually dramatic MITM attacks — they're the subtle ones: certificates expiring at 3am on a Friday, mixed content quietly breaking payment flows after a frontend change, missing HSTS headers allowing downgrade attacks on public networks, or developers testing exclusively on plain HTTP localhost and getting surprised when prod behaves differently.

Most teams treat HTTPS as a checkbox. The engineers who ship reliably are the ones who understand the handshake, certificate validation chains, Certificate Transparency logs, and the exact guarantees (and limitations) TLS actually provides.

What HTTP Is and How a Browser Actually Fetches a Page

HTTP stands for HyperText Transfer Protocol — the agreed-upon set of rules browsers and web servers follow when talking to each other. When you type a URL and hit Enter, the browser does a DNS lookup, opens a TCP connection on port 80, sends a structured text request, and receives a structured text response containing HTML (and eventually CSS, JS, images, etc.).

A typical modern page still triggers dozens of these request-response cycles. Every asset is its own conversation. The critical reality in 2026 is that all of this is still plain text when using HTTP. Anyone on the network path can read it with trivial tools. This is why HTTP-only sites belong only in controlled internal environments or local development.

io/thecodeforge/network/http_request_demo.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
import socket

# io.thecodeforge: Manually craft an HTTP/1.1 request using a raw TCP socket.
# This is exactly what your browser does under the hood (minus all the modern complexity).

HOST = "example.com"
PORT = 80

client_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
client_socket.connect((HOST, PORT))

http_request = (
    "GET / HTTP/1.1\r\n"
    "Host: example.com\r\n"
    "Connection: close\r\n"
    "User-Agent: TheCodeForge-Debug/2026\r\n"
    "\r\n"
)

client_socket.sendall(http_request.encode("utf-8"))

response_parts = []
while True:
    chunk = client_socket.recv(4096)
    if not chunk:
        break
    response_parts.append(chunk)

client_socket.close()

full_response = b"".join(response_parts).decode("utf-8", errors="replace")
header_section, _, body_section = full_response.partition("\r\n\r\n")

print("=== HTTP RESPONSE HEADERS ===")
print(header_section)
print("\n=== FIRST 300 CHARS OF BODY ===")
print(body_section[:300])
Output
=== HTTP RESPONSE HEADERS ===
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 1256
Connection: close
=== FIRST 300 CHARS OF BODY ===
<!doctype html>
<html>
<head>
<title>Example Domain</title>
Mental Model
The HTTP Request Lifecycle
Every HTTP request follows the same loop: resolve DNS, open TCP, send request text, receive response text, close connection (or reuse it in newer versions).
  • DNS resolves the domain to an IP address — the phone book of the internet
  • TCP connection on port 80 is the 'phone line' between browser and server
  • The request is structured text: request line, headers, blank line, optional body
  • The response is also structured text: status line, headers, blank line, body (HTML/CSS/JS)
  • Every asset (image, script, CSS, font) triggers its own request-response cycle
📊 Production Insight
HTTP plaintext means any network intermediary can read passwords, tokens, and API keys in transit. I've seen this bite teams on 'internal' tools that suddenly became reachable from coffee shops or via VPN leaks. Rule of thumb I give every team: never send credentials, tokens, or PII over HTTP — even on networks you control. The cost of enabling TLS is negligible compared to the incident you will eventually have.
🎯 Key Takeaway
HTTP is plaintext — every byte is readable by anyone on the network path. Every asset on a page is a separate request-response cycle on port 80. Never send sensitive data over it. The digital equivalent of sending your passwords on a postcard.
HTTP Request Method Selection
IfRetrieving data without side effects
UseUse GET — cacheable, idempotent, no request body
IfCreating a new resource
UseUse POST — not idempotent, request body contains the new data
IfReplacing an entire resource
UseUse PUT — idempotent, request body is the complete replacement
IfUpdating part of a resource
UseUse PATCH — request body contains only the fields to update
IfRemoving a resource
UseUse DELETE — idempotent, no request body typically
http-https-explained HTTPS Stack: Protecting Credentials Layered security architecture from browser to server Application Layer Browser | Login Form | HTTP Client TLS Handshake Certificate Verification | Key Exchange | Cipher Suite Transport Layer TCP | QUIC (HTTP/3) Network Layer IP Routing | DNS Server Infrastructure Web Server | TLS Termination | Redirect Logic THECODEFORGE.IO
thecodeforge.io
Http Https Explained

Why HTTP Alone Is Dangerous — The Man in the Middle

A Man-in-the-Middle (MITM) attack happens when an attacker inserts themselves between you and the server and can read or modify everything. On plain HTTP this is trivial. Tools like Wireshark, tcpdump, or bettercap make it almost boring. The attacker doesn't need to break any cryptography because there is none.

HTTP has three fatal weaknesses on the public internet: no privacy (everything readable), no integrity (data can be changed silently), and no authentication (you have no proof you're talking to the real server). HTTPS, via TLS, solves all three at once.

io/thecodeforge/network/mitm_illustration.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# io.thecodeforge: Illustrates what an attacker sees with HTTP vs HTTPS
# This is conceptual — no actual packet sniffing.

import os

# === ATTACKER'S VIEW: Plain HTTP ===
http_login_visible = """
POST /login HTTP/1.1
Host: mybank.com
Content-Type: application/x-www-form-urlencoded

username=alice&password=SuperSecret123&token=abc123
"""

print("=== ATTACKER'S VIEW: Plain HTTP ===")
print("Intercepted request (fully readable):")
print(http_login_visible.strip())

print("\n" + "-" * 60)

# === ATTACKER'S VIEW: HTTPS (TLS encrypted) ===
print("=== ATTACKER'S VIEW: HTTPS (TLS encrypted) ===")
print("Intercepted bytes look like random garbage.")
print("They can see destination IP, port 443, and approximate data size.")
print("They cannot read credentials, tokens, or response bodies.")
Output
=== ATTACKER'S VIEW: Plain HTTP ===
Intercepted request (fully readable):
username=alice&password=SuperSecret123&token=abc123
------------------------------------------------------------
=== ATTACKER'S VIEW: HTTPS (TLS encrypted) ===
Intercepted bytes look like random garbage.
They can see destination IP, port 443, and approximate data size.
They cannot read credentials, tokens, or response bodies.
⚠ Watch Out — HTTPS Is Not a Magic Shield
HTTPS only protects data in transit. Once it reaches your servers, its job is done. If you store passwords in plaintext, have broken access control, or ship vulnerable frontend code, HTTPS won't save you. It secures the tunnel. The endpoints and your application logic are still your responsibility.
📊 Production Insight
MITM attacks on HTTP are trivial on shared networks — airports, hotels, conferences, even some corporate guest WiFi. Session cookies sent over HTTP can be stolen and used to impersonate users without ever knowing their password. The rule I enforce on every project: set the Secure flag on all cookies and use __Host- prefix where possible. I've cleaned up too many incidents where this was missed.
🎯 Key Takeaway
HTTP has no privacy, no integrity, and no authentication — three fatal flaws for anything exposed to the public internet. MITM attacks are not theoretical. HTTPS via TLS solves all three problems with one protocol. Treat plain HTTP as legacy infrastructure in 2026.

How HTTPS Works — TLS, Certificates, and the Handshake Explained

HTTPS is not 'HTTP with encryption bolted on.' It is HTTP transported over TLS (Transport Layer Security). TLS delivers the three properties HTTP lacked: privacy (encryption), authentication (certificates), and integrity (message authentication codes).

The TLS handshake lets the client and server agree on a shared session key using asymmetric cryptography without ever sending the key itself. Once established, they switch to fast symmetric encryption for the actual HTTP traffic. The server proves its identity with a certificate signed by a trusted Certificate Authority. In 2026, that certificate is almost always from Let's Encrypt, and browsers expect TLS 1.3 with modern cipher suites.

io/thecodeforge/network/https_connection_demo.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import ssl
import socket
import json

# io.thecodeforge: Manual HTTPS connection with proper TLS verification
# Demonstrates what browsers do automatically.

HOST = "httpbin.org"
PORT = 443

raw_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

tls_context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
tls_context.check_hostname = True
tls_context.verify_mode = ssl.CERT_REQUIRED
# In production you'd also pin or check Certificate Transparency logs

secure_socket = tls_context.wrap_socket(raw_socket, server_hostname=HOST)
secure_socket.connect((HOST, PORT))

print("=== SERVER CERTIFICATE INFO ===")
print(f"Issued to:  {secure_socket.getpeercert().get('subject')}")
print(f"TLS version negotiated: {secure_socket.version()}")

# Send an HTTP request over the now-encrypted channel
http_request = (
    "GET /get HTTP/1.1\r\n"
    f"Host: {HOST}\r\n"
    "Connection: close\r\n"
    "User-Agent: TheCodeForge-Debug/2026\r\n"
    "\r\n"
)
secure_socket.sendall(http_request.encode("utf-8"))

response_chunks = []
while True:
    chunk = secure_socket.recv(4096)
    if not chunk:
        break
    response_chunks.append(chunk)

secure_socket.close()

full_response = b"".join(response_chunks).decode("utf-8", errors="replace")
header_section, _, body_section = full_response.partition("\r\n\r\n")

print("\n=== HTTP RESPONSE STATUS ===")
print(header_section.split("\r\n")[0])
Output
=== SERVER CERTIFICATE INFO ===
Issued to: ((('commonName', 'httpbin.org'),),)
TLS version negotiated: TLSv1.3
=== HTTP RESPONSE STATUS ===
HTTP/1.1 200 OK
Mental Model
The TLS Handshake in 4 Steps
The handshake uses asymmetric crypto to agree on a shared key, then switches to symmetric crypto for speed. TLS 1.3 makes this dramatically faster than the old 1.2 dance.
  • Client sends ClientHello with supported versions, cipher suites, and key share
  • Server replies with ServerHello, its certificate chain, and its own key share
  • Both sides independently compute the same session key (usually via ECDHE)
  • All subsequent data — including the HTTP request — is encrypted with symmetric AES (or ChaCha20) using that key
  • TLS 1.3 does this in 1 round-trip in most cases. 0-RTT is possible but has tradeoffs
📊 Production Insight
TLS 1.2 handshakes with 2 round trips added real latency on high-latency networks. TLS 1.3 is now the default everywhere that matters and cuts that overhead significantly. The gotcha that still bites teams is old load balancers or middleboxes that don't support TLS 1.3 or modern ciphers. Verify with openssl s_client -tls1_3 and monitor your TLS handshake success rate.
🎯 Key Takeaway
TLS gives you encryption, authentication via certificates, and integrity — exactly what plain HTTP lacked. The handshake is asymmetric to safely exchange a key; the actual data transfer is symmetric for performance. In 2026 you should be on TLS 1.3, HTTP/3 where possible, and treating port 443 as the only port that matters for public services.
http-https-explained HTTP vs HTTPS: Credential Exposure Risk Comparing security and performance trade-offs for login HTTP (Insecure) HTTPS (Secure) Credential Encryption Plaintext Encrypted via TLS Man-in-the-Middle Risk High (interception easy) Low (certificate validation) Redirect Requirement Missing redirect exposes 12% 301 redirect enforced Performance Overhead Lower (no encryption) Slightly higher (TLS handshake) HTTP/2 & HTTP/3 Support Limited Full support with multiplexing THECODEFORGE.IO
thecodeforge.io
Http Https Explained

HTTP Status Codes, Request Methods, and Headers You'll Use Daily

Every HTTP conversation consists of a request and a response, both with strict formatting. Methods describe intent. Status codes tell you what happened. Headers carry metadata that makes the whole system work — caching, authentication, content negotiation, security policies.

GET should be safe and idempotent. POST is the workhorse for mutations. PUT and DELETE are idempotent. PATCH is for partial updates. Knowing the difference isn't academic — it affects caching, retry logic, and whether your API is pleasant to use. Status code families (2xx success, 3xx redirection, 4xx client error, 5xx server error) are the first signal when something breaks.

io/thecodeforge/network/http_methods_demo.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import urllib.request
import urllib.error
import json

# io.thecodeforge: Demonstrating HTTP methods with httpbin.org

BASE_URL = "https://httpbin.org"

def make_request(method: str, path: str, payload: dict = None) -> None:
    url = f"{BASE_URL}{path}"
    body_bytes = json.dumps(payload).encode("utf-8") if payload else None

    request = urllib.request.Request(
        url,
        data=body_bytes,
        method=method,
        headers={
            "Content-Type": "application/json",
            "Accept": "application/json",
            "User-Agent": "TheCodeForge-Debug/2026"
        }
    )

    try:
        with urllib.request.urlopen(request) as response:
            status_code = response.status
            response_body = json.loads(response.read().decode("utf-8"))
            print(f"[{method}] {url} → {status_code}")
            if "json" in response_body:
                print(f"  Received: {response_body['json']}")
            else:
                ua = response_body.get('headers', {}).get('User-Agent')
                print(f"  User-Agent seen: {ua}")
    except urllib.error.HTTPError as error:
        print(f"[{method}] {url} → {error.code} {error.reason}")
    print()

make_request("GET", "/get")
make_request("POST", "/post", payload={"name": "Alice", "role": "staff-engineer"})
make_request("PUT", "/put", payload={"name": "Alice Chen", "role": "staff-engineer"})
make_request("DELETE", "/delete")
make_request("GET", "/status/404")
Output
[GET] https://httpbin.org/get → 200
User-Agent seen: TheCodeForge-Debug/2026
[POST] https://httpbin.org/post → 200
Received: {'name': 'Alice', 'role': 'staff-engineer'}
[PUT] https://httpbin.org/put → 200
Received: {'name': 'Alice Chen', 'role': 'staff-engineer'}
[DELETE] https://httpbin.org/delete → 200
[GET] https://httpbin.org/status/404 → 404 NOT FOUND
💡401 vs 403 Is a Classic Interview Trap
401 Unauthorized means 'I don't recognize you — please authenticate.' 403 Forbidden means 'I know exactly who you are, and you are not allowed to do this.' Using 401 when you should use 403 leaks whether an account exists. I've seen security auditors flag this exact pattern.
📊 Production Insight
Misusing 401 vs 403 doesn't just confuse developers — it can leak information about valid usernames. Returning 500 for obvious client errors hides real server bugs in monitoring. Map your auth and authorization failures precisely. The status code is the first thing every client, monitoring system, and future developer will see.
🎯 Key Takeaway
Methods define intent. Status codes report outcome. Headers carry the metadata that makes the modern web work. 401 vs 403 is not interchangeable. Status code families are your first debugging signal. Get these right and everything downstream becomes easier.
HTTP Status Code Selection
IfRequest succeeded and returning data
UseUse 200 OK
IfResource created successfully
UseUse 201 Created with Location header
IfClient sent bad data or missing required fields
UseUse 400 Bad Request with error details in body
IfUser not authenticated (no token or invalid token)
UseUse 401 Unauthorized
IfUser authenticated but lacks permission
UseUse 403 Forbidden
IfServer crashed or unhandled exception
UseUse 500 Internal Server Error — log the stack trace, return generic message to client

HTTP/2 and HTTP/3 — Why Your API Calls Are Faster (and Why They Break Differently)

HTTP/1.1 handles one request per TCP connection. That means head-of-line blocking: one slow asset stalls everything after it. HTTP/2 fixed this with multiplexing — multiple streams over a single connection. No more queueing for CSS while your JSON response sits idle. But TCP itself still has a problem: if a packet drops, all streams pause while TCP retransmits. HTTP/3 throws TCP away entirely. It runs over QUIC on top of UDP. Connections establish in one round trip, not three. Packet loss only affects a single stream, not the whole session. You see this in production when mobile clients lose signal briefly — HTTP/3 resumes without renegotiation. The cost: middleboxes like firewalls and load balancers often block or degrade QUIC traffic. You'll need to test your infrastructure before enabling it. For now, start with HTTP/2 on your CDN and API gateways. It's backward-compatible and gives you most of the speed gain with none of the protocol drama.

Http2Check.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// io.thecodeforge
// Check if your server supports HTTP/2 via ALPN negotiation
import java.net.URI;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;

public class Http2Check {
    public static void main(String[] args) throws Exception {
        HttpClient client = HttpClient.newHttpClient();
        HttpRequest request = HttpRequest.newBuilder()
                .uri(URI.create("https://api.example.com/health"))
                .version(HttpClient.Version.HTTP_2)
                .GET()
                .build();

        HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

        System.out.println("Negotiated version: " + response.version());
        // Output: HTTP_2 if server supports it, else HTTP_1_1
    }
}
Output
Negotiated version: HTTP_2
⚠ Production Trap:
HTTP/2 requires encryption in practice (TLS). If you test with plaintext HTTP/2, most browsers and libraries will silently downgrade to HTTP/1.1. Always check ALPN negotiation on the server side.
🎯 Key Takeaway
HTTP/2 is a drop-in upgrade for most servers. HTTP/3 requires UDP support from your entire network stack — test before you flip the switch.

HTTP Caching — Why Your "Fresh" Data Is Actually Stale and Your Server Is About to Melt

Caching is not an optimization — it's an SLA requirement. Every uncached request hits your database, your compute, or someone else's API. The right HTTP cache headers tell browsers and proxies how long to hold a response before asking again. You control this with Cache-Control: max-age=3600 says "use this response for one hour." The gotcha: cache invalidation is hard. If you set max-age too high, users see stale data. Too low, your server takes the hit anyway. Use ETag headers for validation: the client sends the ETag with an If-None-Match conditional request. If the resource hasn't changed, your server returns 304 Not Modified with zero body. That's a round-trip saved. Never cache user-specific responses with a public directive — that leaks data across tenants. Use private, max-age=0 for authenticated endpoints. For static assets (JS, CSS, images), set max-age to one year and version the URL. If you change the file, change the URL. That breaks the cache cleanly without any invalidation headaches.

CacheHeaders.javaJAVA
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// io.thecodeforge
// Set cache headers on a product listing endpoint
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class ProductController {

    @GetMapping("/api/products")
    public ResponseEntity<String> getProducts() {
        String json = "{\"products\": []}";  // from cache or DB

        return ResponseEntity.ok()
                .header("Cache-Control", "public, max-age=300, must-revalidate")
                .header("ETag", "\"abc123\"")
                .body(json);
    }
}

// Client validates with:
// If-None-Match: "abc123"
// Server responds 304 Not Modified if unchanged
Output
HTTP/1.1 200 OK
Cache-Control: public, max-age=300, must-revalidate
ETag: "abc123"
⚠ Production Trap:
Don't rely on Cache-Control alone. If you deploy a new version of your app, old cached responses may still serve. Always purge CDN caches or change the asset URL on deploy.
🎯 Key Takeaway
Cache aggressively for static assets, but use ETags and 304s for dynamic data. Cache invalidation is not magic — change the URL or bust the cache explicitly.

TLS 1.3 Handshake: What Changed

TLS 1.3, standardized in 2018, revolutionized the handshake process by reducing latency and improving security. Unlike TLS 1.2, which required two round trips (2-RTT) for a full handshake, TLS 1.3 achieves a 1-RTT handshake in most cases, and even 0-RTT for returning clients. This is accomplished by eliminating obsolete cryptographic algorithms and simplifying the handshake flow.

In TLS 1.2, the handshake involved: ClientHello → ServerHello + Certificate + ServerHelloDone → ClientKeyExchange + ChangeCipherSpec + Finished → Server ChangeCipherSpec + Finished. TLS 1.3 combines key exchange and authentication into a single round trip. The client sends its supported key shares (e.g., from Diffie-Hellman parameters) in the initial ClientHello. The server responds with its chosen key share, certificate, and a Finished message, all in one go. This cuts the handshake time in half.

For returning clients, TLS 1.3 supports 0-RTT (early data). The client can send application data immediately after the ClientHello, using a pre-shared key (PSK) from a previous session. However, 0-RTT is vulnerable to replay attacks, so it should only be used for idempotent requests (e.g., GET, but not POST).

Practical example: When a user revisits a website, TLS 1.3 can resume the session with 0-RTT, making page loads feel instant. For APIs, this reduces latency significantly, especially for mobile clients with high latency networks.

Security improvements include removal of weak ciphers (e.g., RC4, 3DES, static RSA) and mandatory forward secrecy via ephemeral Diffie-Hellman. The handshake is also encrypted earlier, hiding the server certificate from eavesdroppers.

tls13_handshake.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
import ssl
import socket

# Create a TLS 1.3 context
context = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
context.minimum_version = ssl.TLSVersion.TLSv1_3
context.load_verify_locations('cert.pem')

# Connect to a server supporting TLS 1.3
with socket.create_connection(('example.com', 443)) as sock:
    with context.wrap_socket(sock, server_hostname='example.com') as ssock:
        print('TLS version:', ssock.version())  # Should print TLSv1.3
        ssock.sendall(b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n')
        print(ssock.recv(1024).decode())
🔥TLS 1.3 Adoption
📊 Production Insight
For high-traffic APIs, enable TLS 1.3 and consider using 0-RTT for read-only endpoints. Monitor for replay attacks and use idempotency keys for safety.
🎯 Key Takeaway
TLS 1.3 reduces handshake latency to 1-RTT (or 0-RTT for repeat visitors) and removes insecure ciphers, making HTTPS faster and more secure.

HTTP/3 and QUIC: The Modern Web Protocol

HTTP/3 is the latest version of HTTP, built on top of QUIC (Quick UDP Internet Connections) instead of TCP. QUIC is a transport protocol developed by Google, standardized as RFC 9000 in 2021. It uses UDP and integrates TLS 1.3 natively, providing encryption by default.

Key advantages over HTTP/2
  • Reduced connection establishment: QUIC combines the cryptographic and transport handshakes into a single 1-RTT (or 0-RTT with a cached connection). HTTP/2 over TCP requires a TCP handshake (1-RTT) followed by TLS 1.3 (1-RTT), totaling 2-RTT.
  • No head-of-line blocking: HTTP/2 multiplexes streams over a single TCP connection, but a lost packet blocks all streams (TCP head-of-line blocking). QUIC runs over UDP and handles packet loss independently per stream, so a lost packet only affects that stream.
  • Connection migration: QUIC connections are identified by a Connection ID, not by IP/port. This allows seamless migration across network changes (e.g., switching from Wi-Fi to cellular) without reconnection.

Practical example: A video streaming service using HTTP/3 can deliver multiple video segments in parallel without one lost packet stalling the entire video. Mobile users switching networks experience no interruption.

HTTP/3 is supported by major browsers (Chrome, Firefox, Safari, Edge) and servers (nginx, LiteSpeed, Cloudflare). However, some corporate firewalls block UDP, so fallback to HTTP/2 is necessary.

Implementation: Use a library like aioquic (Python) or nginx with the quiche module. Clients can use cURL with --http3 flag.

http3_request.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
import asyncio
import aioquic

async def fetch_http3(url):
    # This is a simplified example; actual implementation requires more setup
    async with aioquic.client.connect(url, port=443) as client:
        response = await client.get(url)
        print(response.status_code)
        print(await response.text())

asyncio.run(fetch_http3('https://example.com'))
💡Enabling HTTP/3 on Your Server
📊 Production Insight
Deploy HTTP/3 alongside HTTP/2 for compatibility. Monitor UDP traffic and firewall rules. Use QUIC's 0-RTT for repeat visitors but beware of replay risks.
🎯 Key Takeaway
HTTP/3 over QUIC eliminates TCP head-of-line blocking, reduces connection latency, and supports connection migration, making it ideal for modern web applications.

Certificate Transparency and ACME Protocol

Certificate Transparency (CT) is a framework for publicly logging TLS certificates, enabling anyone to audit Certificate Authorities (CAs). It helps detect misissued or fraudulent certificates. When a CA issues a certificate, it must submit it to multiple public logs. Browsers require certificates to have Signed Certificate Timestamps (SCTs) from these logs to be trusted.

How CT works: The CA sends the certificate to a log server, which returns an SCT (a promise to include the certificate in the log within a Maximum Merge Delay, typically 24 hours). The SCT is embedded in the certificate (via an X.509v3 extension) or delivered via TLS handshake (OCSP stapling). Browsers verify SCTs during TLS handshake; if missing, the connection may be rejected.

ACME (Automatic Certificate Management Environment) protocol automates certificate issuance and renewal, most famously used by Let's Encrypt. ACME allows a server to prove domain control via HTTP challenge (placing a token at a well-known URL) or DNS challenge (adding a TXT record). Once validated, the CA issues a certificate.

Practical example: Using Certbot (ACME client) to obtain a Let's Encrypt certificate: ``bash sudo certbot --nginx -d example.com -d www.example.com `` This automates the entire process: domain validation, certificate issuance, and nginx configuration.

ACME v2 supports wildcard certificates via DNS challenge. Combined with CT, it ensures certificates are publicly logged and automatically renewed before expiry.

Production insight: Set up automated renewal with a cron job or systemd timer. Monitor CT logs for unexpected certificates for your domains using tools like crt.sh.

acme_certbot.shBASH
1
2
3
4
5
6
7
8
9
# Install Certbot and obtain a certificate for nginx
sudo apt update && sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d example.com -d www.example.com

# Test auto-renewal
sudo certbot renew --dry-run

# Check CT logs for your domain
curl -s 'https://crt.sh/?q=example.com&output=json' | jq .
⚠ CT Log Compliance
📊 Production Insight
Use ACME with Let's Encrypt for free, automated certificates. Monitor CT logs for unauthorized certificates. Set up alerts for certificate expiry and renewal failures.
🎯 Key Takeaway
Certificate Transparency ensures public auditability of TLS certificates, while ACME automates issuance and renewal, reducing human error and cost.
● Production incidentPOST-MORTEMseverity: high

Missing HTTP-to-HTTPS Redirect Exposes Login Credentials on Public Wi-Fi

Symptom
Security audit found 12% of login requests arriving over HTTP. Session hijacking incidents spiked on public networks. Support tickets showed unauthorized purchases from unfamiliar devices and locations.
Assumption
The team assumed all traffic was HTTPS because the site displayed a valid certificate and the padlock in testing. They never realized that typing 'example.com' or clicking a plain link defaults to http:// in the browser.
Root cause
No HTTP-to-HTTPS redirect was configured at the edge. The load balancer and web servers listened on port 80 and happily served the full application. No Strict-Transport-Security header was present, so browsers had no memory of preferring HTTPS on future visits. Certificate Transparency logs were being monitored, but that doesn't help if the initial request never uses TLS.
Fix
Implemented a clean 301 redirect from HTTP to HTTPS on all routes at the load balancer level. Added Strict-Transport-Security header with max-age=31536000; includeSubDomains; preload. Submitted the domain to the HSTS preload list. Added monitoring for any HTTP traffic reaching the application servers.
Key lesson
  • Always redirect HTTP to HTTPS at the earliest possible point — never serve real application content on both protocols simultaneously. The redirect itself should be the only thing that ever lives on port 80.
  • HSTS is your memory enforcement layer. Set it with a long max-age on every response (not just the homepage), includeSubDomains, and eventually preload. I've seen teams get burned by setting it only on the root path.
  • Test your setup the way real users and attackers do: curl -I http://yoursite.com should return 301 with a Location header pointing to HTTPS. If you see 200, you're still exposed.
  • HSTS preload protects first-time visitors and removes the initial HTTP window entirely. Submit to the preload list once you're confident in your redirect strategy — it is not easily reversible.
Production debug guideSymptom-driven diagnosis for connection and protocol failures5 entries
Symptom · 01
NET::ERR_CERT_DATE_INVALID or similar expiry errors in browser
Fix
Certificate has expired or is about to. Use openssl s_client or the far more readable ssl-cert-check script to inspect. Renew with certbot renew --force-renewal and reload your services. In 2026 you should have short-lived certs (90 days or less) and automated renewal that alerts at least 7 days early.
Symptom · 02
Mixed content warnings after HTTPS migration
Fix
Some resources (images, scripts, iframes) are still loading via http://. Search your entire codebase and build output. Add Content-Security-Policy: upgrade-insecure-requests as a quick band-aid while you fix the references. Check your CDN configuration too — easy to miss.
Symptom · 03
CORS errors on API calls between HTTP and HTTPS origins
Fix
Browsers treat http://example.com and https://example.com as completely different origins. Standardize on HTTPS everywhere in development and production. The fix is almost always 'use the same protocol as the frontend'.
Symptom · 04
Secure cookies not sent on local development
Fix
Cookies with the Secure flag are ignored over plain HTTP. Stop using plain HTTP for localhost. Use mkcert to generate locally trusted certificates. Your dev environment should match prod behavior as closely as possible.
Symptom · 05
HTTP/2 or HTTP/3 not working despite server support
Fix
Both require HTTPS in all major browsers. Verify with curl --http2 or --http3. Check ALPN negotiation and make sure you're not accidentally falling back due to old cipher configuration or missing TLS 1.3 support.
★ HTTP/HTTPS Quick DebugImmediate actions when HTTP connections or TLS fail
Certificate expired or expiring soon
Immediate action
Check certificate expiry and renew immediately
Commands
echo | openssl s_client -connect yourdomain.com:443 -servername yourdomain.com 2>/dev/null | openssl x509 -noout -enddate
certbot renew --force-renewal --dry-run
Fix now
Set cron: 0 3 * certbot renew --quiet && systemctl reload nginx || kill -HUP $(pgrep nginx)
HTTP-to-HTTPS redirect not working+
Immediate action
Test both protocols and verify redirect configuration
Commands
curl -sI http://yourdomain.com | head -5
curl -sI http://yourdomain.com | grep -i 'location\|strict-transport'
Fix now
Add to Nginx: return 301 https://$host$request_uri; (or equivalent at your CDN/load balancer)
Mixed content blocking page elements+
Immediate action
Find all insecure resource references in source and build output
Commands
grep -rn 'http://' . --include='*.html' --include='*.js' --include='*.css' --include='*.json' | grep -v 'https://'
curl -sI https://yourdomain.com | grep -i content-security-policy
Fix now
Replace http:// references with https:// or protocol-relative URLs. Add CSP header with upgrade-insecure-requests while you clean up.
HTTP vs HTTPS
Feature / AspectHTTPHTTPS
Full nameHyperText Transfer ProtocolHTTP Secure (HTTP over TLS)
Default port80443
Data in transitPlain text — readable by anyone on the networkEncrypted — unreadable without the session key
Server authenticationNone — you can't verify who you're talking toYes — via TLS certificate signed by a trusted CA and checked against Certificate Transparency logs
Data integrityNone — data can be modified in transit silentlyGuaranteed — tampering is detected and rejected
Browser indicatorNo padlock; prominent 'Not Secure' warning in 2026 browsersPadlock icon; URL shown as https://
SEO impactGoogle ranks HTTP sites lowerGoogle uses HTTPS as a positive ranking signal
PerformanceSlightly faster with no handshake (but stuck on HTTP/1.1 behavior)TLS 1.3 + HTTP/3 (QUIC) delivers excellent performance; required for modern features
Certificate costN/AFree via Let's Encrypt with automated renewal; paid options for extended validation or specific use cases
Use case todayInternal development, localhost, or air-gapped networks onlyEverything on the public internet — no exceptions in 2026
⚙ Quick Reference
9 commands from this guide
FileCommand / CodePurpose
iothecodeforgenetworkhttp_request_demo.pyHOST = "example.com"What HTTP Is and How a Browser Actually Fetches a Page
iothecodeforgenetworkmitm_illustration.pyhttp_login_visible = """Why HTTP Alone Is Dangerous
iothecodeforgenetworkhttps_connection_demo.pyHOST = "httpbin.org"How HTTPS Works
iothecodeforgenetworkhttp_methods_demo.pyBASE_URL = "https://httpbin.org"HTTP Status Codes, Request Methods, and Headers You'll Use D
Http2Check.javapublic class Http2Check {HTTP/2 and HTTP/3
CacheHeaders.java@RestControllerHTTP Caching
tls13_handshake.pycontext = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)TLS 1.3 Handshake
http3_request.pyasync def fetch_http3(url):HTTP/3 and QUIC
acme_certbot.shsudo apt update && sudo apt install certbot python3-certbot-nginxCertificate Transparency and ACME Protocol

Key takeaways

1
HTTP is plain text. Every single byte of the conversation can be read by anyone on the network path. Never use it for sensitive data on the public internet in 2026.
2
HTTPS = HTTP + TLS. It simultaneously provides encryption, certificate-based authentication, and integrity. The padlock only proves the first two.
3
The TLS handshake uses asymmetric crypto to safely negotiate a session key, then switches to fast symmetric encryption. This hybrid model is why modern HTTPS has excellent performance, especially with HTTP/3.
4
Status codes, request method semantics, 401 vs 403, and proper HSTS configuration are daily tools, not trivia. The teams that get these details right have dramatically fewer production incidents.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
Can you walk me through exactly what happens when a user types 'https://...
Q02SENIOR
What's the difference between symmetric and asymmetric encryption, and w...
Q03JUNIOR
If a site is served over HTTPS, does that mean it's secure? What are the...
Q04SENIOR
What is HSTS (HTTP Strict Transport Security), and why is it critical fo...
Q05JUNIOR
What happens if a browser encounters a certificate where the Common Name...
Q01 of 05SENIOR

Can you walk me through exactly what happens when a user types 'https://google.com' in their browser and presses Enter — from DNS lookup through to the page rendering?

ANSWER
DNS resolution first — the browser asks for the IP of google.com. Then a TCP connection on port 443, followed by the TLS 1.3 handshake: ClientHello, ServerHello + certificate chain, key exchange via ECDHE, derivation of session keys. The certificate is validated against trusted CAs and Certificate Transparency logs. Once the encrypted channel is up, the browser sends an HTTP/3 or HTTP/2 GET request. The server responds with 200 and the HTML. The browser begins parsing, discovers subresources, and repeats the process (often with session resumption for speed). Each step has failure modes I've debugged in production — DNS hijacking, certificate validation failures, handshake timeouts, and mixed content on subresources.
FAQ · 4 QUESTIONS

Frequently Asked Questions

01
Does HTTPS make my website completely secure?
02
Is HTTP ever acceptable to use in 2026?
03
Why does the browser show a padlock — what has it actually verified?
04
What is the difference between HTTP/1.1, HTTP/2, and HTTP/3?
N
Naren Founder & Principal Engineer

20+ years shipping production systems from the metal up. Notes here come from systems that actually shipped.

Follow
Verified
production tested
July 18, 2026
last updated
2,466
articles · all by Naren
🔥

That's Computer Networks. Mark it forged?

6 min read · try the examples if you haven't

Previous
TCP vs UDP
5 / 30 · Computer Networks
Next
HTTP/2 and HTTP/3