Engineering Notes
Home/Networking & Web/HTTP/HTTP Fundamentals
Networking & Web · HTTP

HTTP Fundamentals

HTTP is a text-based (in 1.1) request/response protocol. Read a raw request and response line by line, learn methods and status-code classes, and see what statelessness means in practice.

🌐 Networking & Web● Beginner⏱ 7 min read▶ 2 interactive🛠️ 3 exercises🎤 5 interview Q&As

🎯 What you'll learn

  • What HTTP is
  • Anatomy of requests and responses
  • Methods, safety and idempotency
  • Status code classes, URLs and statelessness

📚 Before you start

1What HTTP Is

HTTP (HyperText Transfer Protocol) is an application-layer, request-response protocol: a client sends a request, a server sends back exactly one response. It is built on top of a reliable transport (TCP, or QUIC for HTTP/3 — see the HTTP Versions topic) and is stateless — the server does not, by default, remember anything about a previous request when handling the next one. Every piece of context (who you are, what's in your cart) has to be re-sent or reconstructed via mechanisms like cookies (see the Cookies and Sessions topic).

text
Client                              Server
  |------- HTTP Request ----------->|
  |<------ HTTP Response -----------|

2Anatomy of a Request

text
GET /users/42?include=orders HTTP/1.1
Host: api.example.com
User-Agent: Mozilla/5.0
Accept: application/json
Authorization: Bearer eyJhbGciOi...
PartExampleMeaning
MethodGETthe action being requested
Request target/users/42?include=orderspath + optional query string
VersionHTTP/1.1protocol version in use
HeadersHost: ..., Accept: ...metadata, one per line
Body(empty for GET)payload, present for POST/PUT/PATCH

A blank line separates headers from the body — this is how the parser knows headers have ended, even before reading Content-Length bytes of body.

3Anatomy of a Response

text
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 58
Cache-Control: max-age=60

{"id":42,"name":"Ada Lovelace","orders":[101,102]}
PartExampleMeaning
VersionHTTP/1.1protocol version the server used
Status code200machine-readable outcome
Reason phraseOKhuman-readable label (advisory only)
HeadersContent-Type: ...metadata about the body
Bodythe JSONactual payload

4HTTP Methods

MethodPurposeSafeIdempotentHas a body
GETretrieve a resourceyesyesno (by convention)
HEADlike GET, headers onlyyesyesno
POSTcreate a resource / trigger an actionnonoyes
PUTreplace a resource entirelynoyesyes
PATCHpartially update a resourcenono*yes
DELETEremove a resourcenoyesno (by convention)
OPTIONSask what methods/headers are allowedyesyesno

Safe means the method must not change server state (a GET that increments a view counter is a real-world violation of this, not an exception to it). Idempotent means firing the same request N times has the same effect as firing it once — DELETE /users/42 twice still ends with user 42 gone, so it's idempotent even though it's not safe. PATCH is not idempotent in general because a patch like {"balance": "+10"} (relative, not absolute) applied twice produces a different result than applied once — though a PATCH expressed as an absolute field replacement can be idempotent in practice.

5Status Code Categories

RangeCategoryExamples
1xxInformational100 Continue, 101 Switching Protocols
2xxSuccess200 OK, 201 Created, 204 No Content
3xxRedirection301 Moved Permanently, 304 Not Modified
4xxClient Error400 Bad Request, 401 Unauthorized, 404 Not Found
5xxServer Error500 Internal Server Error, 503 Service Unavailable

The first digit tells a client (or a proxy that never parses the body) how to broadly react without understanding the specific code — this is deliberate: a generic HTTP client can treat any unrecognized 4xx as "my request was wrong" and any unrecognized 5xx as "retry might help" purely from that leading digit.

Client sends a request across a TCP connection, server sends back a response, connection is reused for the next request
Client sends a request across a TCP connection, server sends back a response, connection is reused for the next request

6URLs, the Request Target

text
https://api.example.com:443/v1/users/42?include=orders#profile
\___/   \_______________/ \_/\_____________/\_____________/\_____/
scheme        host        port     path          query      fragment

Only scheme, host, and path (plus query) typically reach the server — the fragment (#profile) is resolved entirely client-side and is never sent in the actual HTTP request line; a server has no way to see it.

7Statelessness in Practice

Because HTTP itself has no memory, "session" behavior is always built on top of it, never inside it: a cookie is just a header value the client happens to re-send on every subsequent request, and a JWT is just a token the client re-attaches to Authorization. The protocol never tracks "this is the third request from the same visitor" — every request is judged solely on the headers/body present in that one request.

Under the HoodWhat Actually Crosses the Wire▾

Before any HTTP bytes: the TCP handshake

For HTTP/1.1 and HTTP/2 (both run over TCP), no HTTP data can be sent until a TCP connection exists. That's a real 3-step exchange:

text
Client -> Server   SYN            (client proposes a connection)
Server -> Client   SYN-ACK        (server acknowledges, proposes back)
Client -> Server   ACK            (client acknowledges — connection is now open)

Only after this round trip completes does the client write the actual GET / HTTP/1.1\r\n... bytes onto the now-open socket. This is one full network round trip spent before a single byte of HTTP exists — the reason HTTPS adds a further TLS handshake round trip on top, and the reason connection reuse (Connection: keep-alive, the default in HTTP/1.1) matters: without it, every request pays this handshake cost again.

HTTP/1.1's request format is literally text, CRLF-terminated

There is no framing layer, no length-prefixed fields — the parser reads the socket byte-by-byte looking for \r\n (carriage return + line feed) to end each header line, and a bare \r\n\r\n to mark "headers are done, body starts now." This is why a header injection vulnerability is possible if user input containing a raw \r\n ever gets concatenated unescaped into a header value — the parser has no other way to know where a line ends, so injected \r\n sequences can forge additional headers.

text
GET / HTTP/1.1\r\n
Host: example.com\r\n
\r\n

Connection reuse (keep-alive)

In HTTP/1.0, a new TCP connection (and thus a new handshake) was opened per request by default — expensive. HTTP/1.1 makes persistent connections the default: the same TCP socket serves multiple sequential request/response pairs, avoiding a repeated handshake for every asset a page needs. The client and server both need Content-Length (or Transfer-Encoding: chunked) on every response specifically so the reader knows exactly where one response ends and the next one can begin on the same socket — without a known length, there'd be no way to tell "response body" apart from "start of the next response."

⚠️ Common Pitfalls

  1. Treating GET as always side-effect-free in practice — it's a convention the spec requires, not something the protocol enforces; a poorly built API can still mutate state on GET, which breaks caching, prefetching, and browser back/forward behavior that all assume GET is safe.
  2. Confusing "idempotent" with "has no effect the second time" — idempotent means the end state is the same, not that the second call does nothing; DELETE /users/42 called twice still returns some response the second time (often 404), it just doesn't change server state further.
  3. Assuming the fragment (#...) reaches the server — it never does; it's resolved purely in the browser, which is exactly why it's used for client-side routing state.
  4. Forgetting the response needs Content-Length or chunked encoding — without one of these, an HTTP/1.1 client reading a persistent connection has no reliable way to know where the response body ends.

📋 Quick Reference

text
Request:  METHOD /path?query HTTP/version\r\n  Headers\r\n  \r\n  [body]
Response: HTTP/version STATUS reason\r\n        Headers\r\n  \r\n  [body]

Safe + idempotent:      GET, HEAD, OPTIONS
Idempotent, not safe:   PUT, DELETE
Neither:                POST, (usually) PATCH

1xx info   2xx success   3xx redirect   4xx client error   5xx server error

🧠 Key takeaways

  • A message = start line + headers + blank line + optional body.
  • Methods: GET/HEAD safe; PUT/DELETE idempotent; POST neither (use idempotency keys).
  • Status classes: 2xx success, 3xx redirect, 4xx client error, 5xx server error.
  • HTTP is stateless: sessions come from cookies or tokens sent on each request.

🎤 Interview Q&A

Questions interviewers ask about this topic. Answer out loud first, then open a card to compare.

Q1What is HTTP and what does a request look like?core▾

A stateless, text-based (in HTTP/1.1) request/response protocol. A request has a method, path, version, headers and optional body: GET /users/42 HTTP/1.1, Host: api.example.com. A response has a status code, headers and body.

Follow-ups interviewers ask
  • What does stateless mean? Each request carries all needed context; servers don't remember previous requests (state is in cookies/tokens/DB).
Q2What are safe and idempotent methods?core▾

Safe methods don't change state: GET, HEAD, OPTIONS. Idempotent methods give the same result if repeated: GET, PUT, DELETE (plus safe ones). POST and PATCH are neither by default.

Follow-ups interviewers ask
  • Why does it matter? Clients, proxies and retries can repeat idempotent requests safely.
Q3Explain the status code classes.core▾

1xx informational, 2xx success (200, 201, 204), 3xx redirection (301, 302, 304), 4xx client errors (400, 401, 403, 404, 409, 422, 429), 5xx server errors (500, 502, 503, 504).

Follow-ups interviewers ask
  • 502 vs 504? 502: the upstream returned an invalid response; 504: the upstream timed out.
Q4What's the difference between 401 and 403?senior▾

401 Unauthorized means not authenticated (missing/invalid credentials; include WWW-Authenticate). 403 Forbidden means authenticated but not allowed. Some APIs return 404 instead of 403 to avoid revealing a resource exists.

Follow-ups interviewers ask
  • When return 404 for forbidden resources? When existence itself is sensitive.
Q5How does keep-alive improve performance?core▾

Persistent connections reuse one TCP (and TLS) connection for many requests, avoiding handshake round trips per request. HTTP/1.1 keeps connections alive by default.

Follow-ups interviewers ask
  • Limitation in HTTP/1.1? One request at a time per connection, so browsers open ~6 connections per host.

🛠️ Practice exercises

Each exercise is a file in this topic folder. Code exercises are self-checking: run them and make the checks pass.

✅ Check yourself

Answer the questions — feedback is instant.

1. Which header is mandatory in HTTP/1.1 requests?
Virtual hosting.
2. Which method is idempotent but not safe?
Repeating PUT gives the same state.
3. Authenticated user lacks permission → status?
401 = not authenticated.
4. What separates headers from the body?
Blank line.