02) Understanding HTTP for Backend Engineers — Where It All Starts
Backend is… huge.
If we try to talk about everything that could possibly fall under “backend engineering,” we’ll be here for years. So instead of chasing every edge case, it makes more sense to focus on the pieces that show up almost everywhere — the things you’ll run into in most real-world codebases, regardless of language or framework.
That’s the lens I’m using here.
And the first stop in that landscape is HTTP — the medium through which browsers and servers talk to each other, exchange data, and pretend that everything is simple when it really isn’t.
The idea that quietly runs the web
HTTP looks straightforward on the surface. You send a request, you get a response. But under that simplicity are two ideas that shape almost everything built on top of it.
The first is statelessness.
HTTP has no memory. It doesn’t remember who you are, what you did five seconds ago, or what the last request looked like. Every request is treated as a brand-new event. That’s why each request must carry all the information the server needs — URLs, headers, authentication tokens, session identifiers, everything.
Once the server responds, it forgets.
This turns out to be a feature, not a limitation. Stateless systems are simpler to reason about, easier to scale, and far more resilient. If one server crashes, another can pick up the next request without worrying about restoring state. That’s also why things like cookies, tokens, and sessions exist — they’re ways to simulate continuity on top of a protocol that fundamentally doesn’t care about the past.
The second idea is the client–server model.
Communication is always initiated by the client. A browser, a mobile app, a script — something asks. The server listens, processes, and responds. That’s it. The server never “pushes” first in classic HTTP.
HTTPS doesn’t change this model. It’s simply HTTP wrapped in encryption. Same rules, same flow — just safer.
How messages actually move
Before a request or response even exists, the client and server need a way to communicate. That happens over the network, usually using TCP — a reliable, connection-based transport protocol.
HTTP itself doesn’t care how data is transported, only that it arrives reliably. In practice, TCP fits that requirement well, which is why HTTP is layered on top of it.
This layering is often explained using the OSI model. Backend engineers mostly live at the application layer — the top — while things like TCP handshakes and TLS encryption happen below. Knowing that those layers exist is useful. Living inside them usually isn’t necessary.
HTTP keeps evolving, quietly
HTTP didn’t arrive fully formed.
HTTP/1.0 opened and closed a connection for every request. Predictably slow.
HTTP/1.1 introduced persistent connections, chunked transfer encoding, and better caching. One connection, many requests.
HTTP/2 added multiplexing, binary framing, header compression, and server push — multiple requests flowing together without blocking each other.
HTTP/3 moved even lower, replacing TCP with QUIC over UDP to reduce latency and improve loss handling.
You don’t need to memorize these versions. The important thing to remember is this: clients and servers establish a connection, exchange messages, and the protocol keeps getting better at doing that efficiently.
What an HTTP message really contains
Every HTTP interaction boils down to messages.
A request message includes:
- a method
- a resource URL
- the HTTP version
- headers
- an optional body
A response message includes:
- the HTTP version
- a status code
- headers
- an optional body
Headers deserve special attention. They’re key–value pairs that carry metadata about the request or response. Instead of stuffing everything into URLs or bodies, headers separate what the message is from what the message is about.
A useful mental model is parcel delivery. You don’t put the address inside the package — you write it on top. Headers work the same way.
Why headers matter more than they look
Headers tell servers who you are, what you want, what you can handle, and how the response should behave.
Some headers describe the request itself — user agent, authorization tokens, accepted content types.
Some are general — caching rules, connection behavior, timestamps.
Some describe the body — content type, content length, encoding, entity tags.
Some exist purely for security — enforcing HTTPS, preventing XSS, protecting cookies, stopping clickjacking.
Two properties make headers especially powerful.
First, extensibility. New headers can be introduced without changing the protocol. Security improvements, custom behaviors, content negotiation — all layered on top.
Second, control. Headers act like a remote control. They influence how servers respond, how browsers behave, and how resources are cached, secured, or transformed.
Intent, not just action
HTTP methods exist to express intent.
GET means fetch data without changing anything.
POST means create something new.
PATCH means update part of a resource.
PUT means replace it entirely.
DELETE removes it.
There’s nuance here, especially around idempotency.
Some methods can be safely repeated without changing the outcome — GET, PUT, DELETE. Others can’t. POST, by design, creates new results each time.
Understanding this distinction helps with retries, caching, and correctness — especially when systems get distributed.
OPTIONS exists mostly behind the scenes, powering one of the most misunderstood flows on the web: CORS.
Why browsers sometimes say “CORS error”
Browsers enforce the same-origin policy. By default, a web page can’t talk to a server on a different origin.
CORS exists to relax that rule — but only when the server explicitly allows it.
Sometimes a request goes straight through. That’s a simple request.
Other times, the browser sends a preflight request first — an OPTIONS request — asking the server what it supports. Which methods are allowed? Which headers? For how long should this permission be cached?
If the server answers correctly, the browser proceeds. If not, the browser blocks the response — even if the server technically sent data.
Nothing here is random. It’s all driven by headers, methods, and explicit permission.
Status codes are not decoration
HTTP status codes exist so clients don’t have to guess.
They’re standardized signals that say:
- this worked
- this didn’t
- try again later
- you’re not allowed
- something broke on the server
200 means success.
201 means something was created.
204 means success, no content.
301 and 302 redirect.
304 means “use your cached version.”
400-level codes point to client-side problems — bad data, missing auth, forbidden access, invalid methods, conflicts, rate limits.
500-level codes signal server-side failures — crashes, unavailable services, timeouts, proxy failures.
Once you start reading systems through status codes, debugging becomes much faster.
Caching is a conversation, not a guess
HTTP caching is based on cooperation.
Servers tell clients how long something can be cached. Clients ask if their cached version is still valid.
Headers like Cache-Control, ETag, and Last-Modified enable conditional requests. Instead of downloading the same data again, the client asks: Has this changed?
If it hasn’t, the server responds with 304 Not Modified. No body. No bandwidth wasted.
Modern tools often abstract this away, but understanding the underlying flow makes those abstractions easier to trust — and debug.
When formats, languages, and size matter
Clients don’t all want the same thing.
Some want JSON. Others XML. Some prefer English. Others Spanish. Some support compression. Others don’t.
Content negotiation lets clients express preferences through headers — and servers respond accordingly.
Compression works the same way. Large responses can be compressed dramatically, saving bandwidth and time. Without it, payload sizes explode.
Connections, files, and streams
HTTP 1.1 introduced persistent connections so multiple requests could reuse the same TCP connection. Less overhead. Better performance.
For large uploads, multipart requests split binary data into parts so servers can reconstruct files safely.
For large downloads or live data, chunked transfer streams responses piece by piece, keeping connections open until everything arrives.
All of this happens quietly — but it shapes how systems feel in practice.
Security, without the ceremony
SSL came first. It had flaws. TLS replaced it.
HTTPS is simply HTTP over TLS.
The encryption layer handles certificates, authentication, and secure channels. From an application point of view, the request–response model doesn’t change — only the safety does.
Knowing that is enough to work confidently at the application layer.
The bigger picture
Once you internalize how HTTP works — not just the syntax, but the flow — frameworks stop feeling magical. Codebases become navigable. Bugs become explainable.
This isn’t about memorizing rules.
It’s about building a mental map — one that stays useful no matter which language, framework, or stack you encounter next.
And this is just the beginning of that map.
