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.
🎯 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).
Client Server
|------- HTTP Request ----------->|
|<------ HTTP Response -----------|2Anatomy of a Request
GET /users/42?include=orders HTTP/1.1
Host: api.example.com
User-Agent: Mozilla/5.0
Accept: application/json
Authorization: Bearer eyJhbGciOi...
| Part | Example | Meaning |
|---|---|---|
| Method | GET | the action being requested |
| Request target | /users/42?include=orders | path + optional query string |
| Version | HTTP/1.1 | protocol version in use |
| Headers | Host: ..., 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
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]}| Part | Example | Meaning |
|---|---|---|
| Version | HTTP/1.1 | protocol version the server used |
| Status code | 200 | machine-readable outcome |
| Reason phrase | OK | human-readable label (advisory only) |
| Headers | Content-Type: ... | metadata about the body |
| Body | the JSON | actual payload |
4HTTP Methods
| Method | Purpose | Safe | Idempotent | Has a body |
|---|---|---|---|---|
GET | retrieve a resource | yes | yes | no (by convention) |
HEAD | like GET, headers only | yes | yes | no |
POST | create a resource / trigger an action | no | no | yes |
PUT | replace a resource entirely | no | yes | yes |
PATCH | partially update a resource | no | no* | yes |
DELETE | remove a resource | no | yes | no (by convention) |
OPTIONS | ask what methods/headers are allowed | yes | yes | no |
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
| Range | Category | Examples |
|---|---|---|
| 1xx | Informational | 100 Continue, 101 Switching Protocols |
| 2xx | Success | 200 OK, 201 Created, 204 No Content |
| 3xx | Redirection | 301 Moved Permanently, 304 Not Modified |
| 4xx | Client Error | 400 Bad Request, 401 Unauthorized, 404 Not Found |
| 5xx | Server Error | 500 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.
6URLs, the Request Target
https://api.example.com:443/v1/users/42?include=orders#profile
\___/ \_______________/ \_/\_____________/\_____________/\_____/
scheme host port path query fragmentOnly 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:
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.
GET / HTTP/1.1\r\n
Host: example.com\r\n
\r\nConnection 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
- 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.
- 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/42called twice still returns some response the second time (often404), it just doesn't change server state further. - 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. - Forgetting the response needs
Content-Lengthor 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
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
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.
- 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.
- 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).
- 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.
- 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.
- 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.