I used the web for years without really understanding it.
I could build apps.
I could ship features.
But if someone asked “what actually happens when you type a URL and hit enter?” — I’d give a half-correct answer and hope the conversation moved on.
This post is the explanation I wish I had earlier.
No history lesson. No protocol trivia. Just how browsers and servers actually talk.
We usually say:
“The browser sends a request. The server sends a response.”
That’s not wrong.
It’s just useless.
Real systems break when you don’t understand what’s in between.
Step 1: the browser doesn’t know your server
When you type:
Your browser has no idea where that server lives.
So it asks DNS:
“Hey, what IP address owns collabspace.app?”
DNS responds with something like:
203.0.113.42
That’s it.
No magic. Just a phonebook lookup.
If DNS is slow or broken, your app feels slow, even if your backend is perfect.
Step 2: TCP before HTTP (this part matters)
Before any HTTP request exists, the browser does this:
opens a TCP connection to 203.0.113.42
negotiates encryption (TLS) if it’s HTTPS
This handshake costs time.
That’s why:
cold starts feel slow
HTTP/2 and HTTP/3 exist
connection reuse matters more than people think
You can’t optimize what you don’t know exists.
Step 3: HTTP is just text (mostly)
A request looks like this:
GET /api/documents/123 HTTP/1.1
Host: collabspace.app
Authorization: Bearer token
That’s it.
No functions.
No objects.
Just structured text over a connection.
Frameworks hide this, but bugs leak through when headers, methods, or status codes are wrong.
Step 4: your backend is not “the server”
The request doesn’t magically hit your Next.js route.
It goes through layers:
load balancer
reverse proxy (nginx / vercel edge)
app server
middleware
route handler
database / cache
When something breaks, it’s usually not your code.
It’s one of the layers you forgot existed.
Step 5: stateless by default (yes, really)
Each HTTP request is independent.
The server does not remember the user.
So we fake memory using:
cookies
headers
tokens
sessions
Redis
If you ever wondered why auth is annoying — this is why.
The web was never designed for “logged-in users”.
We hacked that in later.
Step 6: responses are just decisions
A response is simply:
status code
headers
body
Example:
200 OK
Content-Type: application/json
Status codes aren’t decoration.
If you misuse them:
caching breaks
retries break
clients behave weirdly
Most “random frontend bugs” are actually bad backend responses.
Where WebSockets change the game
HTTP is request → response → done.
Real-time apps don’t fit that model.
So WebSockets do this instead:
open one connection
keep it alive
send messages both ways
That’s how collaborative apps, chats, and multiplayer tools work.
Not “real-time APIs”.
Just persistent connections.
Once you get this, WebSockets stop feeling scary.
Why this matters if you’re “full-stack”
If you build full-stack apps and don’t understand this:
debugging feels random
performance feels mystical
system design feels abstract
Once you do:
logs make sense
network tabs become useful
architecture decisions get easier
This knowledge pays off forever.
My rule of thumb
If you can’t explain:
DNS
TCP vs HTTP
stateless requests
persistent connections
…you’re using the web, not building on it.
That’s fine early on.
But at some point, you should cross that line.
Final thought
Frameworks are great.
Abstractions are useful.
But the browser and server don’t care about your stack.
They only care about:
bytes
connections
rules
Learn those once.
They never change.
If you liked this
I’m building CollabSpace, a real-time collaborative app, and sharing everything I learn in public.
Next posts coming:
building a real-time collaborative app from scratch
why i stopped collecting frameworks and started shipping
what “full-stack engineer” actually means in 2026
Follow me if you want less fluff and more real engineering.
Top comments (0)