Most developers skip this. Here's why you shouldn't.
Before I learned Flask, before I wrote a single line of backend code, my teacher asked me a question I couldn't answer:
"What actually happens when you type a URL and press Enter?"
I said something like "the browser loads the page."
He smiled. That was the wrong answer — or at least, a very incomplete one.
Here's what I now know happens in that half-second between pressing Enter and seeing a webpage. We'll walk through every layer — from a string of characters in your address bar to pixels lit up on your screen — and along the way I'll point out the bits that matter most when you start writing backend code.
The browser parses the URL
It's not just text — it's a tiny structured object.
URL parser — click to tear apart
When you press Enter, the browser tears the URL apart into pieces: the scheme (https), the host (stripe.com), an optional port, a path, a query string, and a fragment. If you typed something that isn't a valid URL, the browser hands it off to your default search engine instead.
1const url = new URL("https://stripe.com/pricing?ref=blog#enterprise");2 3url.protocol; // "https:"4url.host; // "stripe.com"5url.pathname; // "/pricing"6url.search; // "?ref=blog"7url.hash; // "#enterprise"url.pathname. The browser already did most of the parsing — your framework just dispatches on it.DNS turns the name into an address
The internet doesn't speak in words. It speaks in numbers.
Servers are reachable by IP address, not by name. So the browser asks: "what IP belongs to stripe.com?" That question travels through a chain of caches before it ever hits a real DNS server.
DNS resolution flow
Most of the time the answer comes from a cache one or two steps in — your browser's, your OS's, or your ISP's recursive resolver. Only on a cold lookup do you walk all the way up to the root and back down through the TLD.
TCP opens a reliable pipe
Three packets later, you have a connection.
Now the browser has an IP. It opens a TCP connection — the protocol that guarantees bytes arrive in order, exactly once, even over an unreliable network. The opening ritual is the famous three-way handshake.
TCP three-way handshake
Connection established. Both sides agree on sequence numbers.
Each round-trip costs whatever your latency is. If your server is in Frankfurt and you're in São Paulo, that's ~200ms just to say hello. This is one reason CDNs exist: terminate TCP closer to the user, then reuse a warm connection to the origin.
TLS makes it private
Because plain HTTP is a postcard. HTTPS is a sealed envelope.
HTTPS is just HTTP on top of TLS. Before any HTTP byte is sent, both sides exchange a few more messages to agree on a cipher, verify the server's certificate, and derive a shared session key.
TLS handshake
Encrypted session key established. All future bytes are encrypted.
curl -k but browsers will refuse them. In Flask dev, always test against a real cert (mkcert is your friend) before shipping.The browser sends an HTTP request
A few lines of text — that's all a request really is.
With an encrypted pipe in place, the browser finally sends what we usually think of as "the request": a method, a path, a stack of headers, and (sometimes) a body. It's plain text. You can read it.
GET /pricing HTTP/1.1 Host: stripe.com User-Agent: Mozilla/5.0 Accept: text/html Accept-Encoding: gzip Cookie: session=…
HTTP/1.1 200 OK Content-Type: text/html; charset=utf-8 Content-Encoding: gzip Cache-Control: max-age=3600 Server: nginx <!doctype html>…
1from flask import Flask, request2 3app = Flask(__name__)4 5@app.route("/pricing")6def pricing():7 # Everything you saw on the left is now available here8 print(request.method) # "GET"9 print(request.headers) # all those Header: value lines10 print(request.cookies.get("session"))11 return render_template("pricing.html")The server figures out what to send back
This is where your code finally runs.
The request hits a load balancer, gets routed to one of many app processes, walks through middleware, matches a route, runs your view function, hits a database, renders a template, and ships bytes back down the same TCP connection — usually compressed, often cacheable.
1# What Flask actually sends on the wire2HTTP/1.1 200 OK3Content-Type: text/html; charset=utf-84Content-Encoding: gzip5Cache-Control: public, max-age=36006Set-Cookie: session=abc123; HttpOnly; Secure7Content-Length: 143028 9<!doctype html><html>...</html>The browser renders the page
Bytes become pixels through a long, beautiful pipeline.
The HTML arrives as a stream. The browser starts parsing immediately — it doesn't wait for the whole document. As it discovers<link>,<script>, and<img> tags, it fires off more requests in parallel and tries to keep the main thread busy laying out what it already has.
Network waterfall (browser rendering)
The DOM and CSSOM are built, combined into a render tree, laid out, painted, and composited. Anywhere along this chain a slow font, a render-blocking script, or a giant hero image can ruin everything.
The full picture
Half a second, eight protocols, one moment of magic.
None of these steps are optional. Skip DNS and you don't know where to go. Skip TCP and your bytes scatter. Skip TLS and the internet reads your password. Skip rendering and you stare at raw HTML.
The reason senior engineers seem to magically know where to look when a request is slow is that they've internalized this map. Now you have it too.