How the Web Works
What you'll build
By the end of this lesson you will be able to run a tiny local web server, open it in your browser, watch the HTTP request in DevTools, and explain every step that happened, from typing a URL to seeing the page. You will also know how to use curl to inspect raw HTTP responses, which is the fastest debugging tool you will ever learn.
Concepts
The client-server model
A "client" is any program that asks for something. A "server" is any program that answers. Your browser is a client. When you open https://class-central.com, the browser sends a request to a server somewhere on the internet, and the server sends back HTML.
That is it. Every web interaction, loading a page, submitting a login form, streaming a video, is just a client asking and a server answering, repeatedly.
Browser Web Server
|------ GET /index.html --------->|
|<----- 200 OK + HTML body -------|
DNS: how names become numbers
Computers talk to each other using IP addresses like 142.250.195.46. Humans prefer names like google.com. DNS (Domain Name System) is the phone book that maps one to the other.
When you type google.com and press Enter, your OS asks a DNS resolver (usually your ISP's, or 8.8.8.8) to translate that name into an IP. This takes maybe 1, 50 ms on a first visit. The result is cached so subsequent visits are instant.
You can inspect this yourself:
# Ask Google's public DNS what IP hosts class-central.com
nslookup class-central.com 8.8.8.8
# Or with the dig tool (more detail)
dig class-central.com
HTTP: the language of the web
HTTP (Hypertext Transfer Protocol) is a plain-text protocol. A request looks like this:
GET /courses HTTP/1.1
Host: www.class-central.com
Accept: text/html
And the response looks like:
HTTP/1.1 200 OK
Content-Type: text/html; charset=utf-8
Content-Length: 12483
<!DOCTYPE html>
<html>...
The first line of a response always has a status code. The ones you will hit every day:
| Code | Meaning |
|---|---|
| 200 | OK, everything worked |
| 301 | Moved permanently, follow the redirect |
| 404 | Not found, the URL does not exist on the server |
| 500 | Internal server error, the server crashed |
What the browser does on Enter
This is the full sequence. Understanding it lets you locate bugs at the right layer:
- Parse the URL, scheme (
https), host (class-central.com), path (/courses). - DNS lookup, resolve the hostname to an IP.
- TCP connection, open a reliable connection to port 443 (HTTPS) or 80 (HTTP).
- TLS handshake (HTTPS only), verify the server's certificate, establish encryption.
- Send the HTTP request,
GET /courses HTTP/1.1. - Receive the response, status line, headers, then the body (HTML).
- Parse and render, the browser builds the DOM from HTML, fetches CSS and JS referenced in that HTML, runs the JS, and paints pixels.
Steps 1, 6 happen before you see anything. That is why a slow DNS server or a missing TLS certificate can make a page feel broken even before a single byte of HTML arrives.
Using curl to see raw HTTP
curl is a command-line tool that makes HTTP requests and prints the response. It is your best friend for debugging APIs and servers.
# Basic GET, prints the JSON body
curl https://jsonplaceholder.typicode.com/todos/1
# Show headers too
curl -i https://jsonplaceholder.typicode.com/todos/1
# Follow redirects, show timing
curl -L -w "\n\nTotal time: %{time_total}s\n" https://google.com -o /dev/null -s
Hands-on
Let's serve a real web page locally and watch what happens in the browser.
Step 1: Create a tiny HTML file.
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>My First Server</title>
</head>
<body>
<h1>Hello from my server!</h1>
<p>The browser fetched this from localhost:8000.</p>
</body>
</html>
Step 2: Start Python's built-in HTTP server in the same folder.
python3 -m http.server 8000
You will see: Serving HTTP on 0.0.0.0 port 8000.
Step 3: Open http://localhost:8000 in your browser.
You will see the page. Now switch to the terminal, you will see the log line:
127.0.0.1 - - [10/May/2026 10:22:01] "GET / HTTP/1.1" 200 -
That is the raw HTTP request your browser sent, logged by the server.
Step 4: Open DevTools (F12) → Network tab → reload the page.
Click on the first request (localhost). You will see:
- Request Headers, what the browser sent, including
Host,User-Agent,Accept. - Response Headers, what the server replied with, including
Content-TypeandContent-Length. - Status Code, 200.
- Timing, how long each step took.
Try changing http://localhost:8000/index.html to http://localhost:8000/nope.html. You will see a 404 in the terminal and in DevTools. That is the server saying "I do not have a file called nope.html".
Step 5: Do the same request with curl.
curl -i http://localhost:8000/index.html
Output:
HTTP/1.0 200 OK
Server: SimpleHTTP/0.6 Python/3.12.0
Date: Sat, 10 May 2026 04:52:01 GMT
Content-type: text/html
Content-Length: 196
Last-Modified: Sat, 10 May 2026 04:50:00 GMT
<!DOCTYPE html>
<html lang="en">
...
Everything above the blank line is the header. Everything after is the body. When you understand this separation you can debug almost any web problem.
Common pitfalls
- Confusing HTTP and HTTPS. A browser that loads
http://sends your data in plain text. Use HTTPS in production always. Locally, plain HTTP is fine. - Thinking 404 means the server is down. It means the server is up but that specific file does not exist. Check your path.
- DNS cache confusing you. If you change a DNS record and your browser still shows the old site, run
ipconfig /flushdns(Windows) orsudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder(Mac). Or just open an incognito tab and wait a few minutes. - Port conflicts. If
python3 -m http.server 8000says "Address already in use", another process owns port 8000. Use 8080 or 9000 instead. - CORS errors in the browser, but curl works fine. CORS is a browser-only restriction. The server response is the same; the browser just refuses to give it to your JavaScript. This is expected behaviour, not a server bug.
What to try next
- Modify
index.htmlto link to astyle.cssfile. Watch two separate requests appear in DevTools when you reload, one for HTML, one for CSS. - Use
curl -X POST -d '{"name":"test"}' -H "Content-Type: application/json" https://jsonplaceholder.typicode.com/postsand read the JSON response. This is how every form submission and API call works under the hood. - Look up the HTTP status code 418 ("I'm a teapot") and find which RFC defines it. This will teach you that RFCs, the actual specifications behind HTTP, are very readable documents, not just dry bureaucracy.
Prefer watching over reading?
Subscribe for free.