HTTP: The Web’s Contract
HTTP (HyperText Transfer Protocol) is the application-layer protocol of the web — a simple, text-based request/response exchange over TCP. Nearly every API, every browser, every microservice, and every load balancer speaks it. Understanding HTTP is understanding how the world’s largest distributed system actually communicates.
The Request/Response Cycle
A request is a method + target + headers (+ optional body); a response is a status line + headers (+ optional body):
GET /products/42 HTTP/1.1
Host: api.example.com
Accept: application/json
User-Agent: curl/8.0
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 37
{"id": 42, "name": "Widget", "price": 9.99}
Both are stateless: each request carries everything the server needs. State (sessions, identity) is layered on top with cookies or tokens — not held in the server’s memory between requests. Statelessness is what makes HTTP trivially load-balanced across many servers.
Methods and Their Meaning
| Method | Semantics | Safe? | Idempotent? |
|---|---|---|---|
| GET | Retrieve a resource | Yes | Yes |
| HEAD | Headers only | Yes | Yes |
| OPTIONS | Capabilities / preflight | Yes | Yes |
| PUT | Replace a resource | No | Yes |
| DELETE | Remove a resource | No | Yes |
| POST | Create / action / process | No | No |
| PATCH | Partial update | No | No |
- Safe = no side effects (caches/proxies may prefetch).
- Idempotent = repeating it produces the same result (so retries are safe).
This table is the answer to a hundred API-design questions: retries on POST need idempotency keys (covered in the API Design topic); PUT is safe to retry; GET is cacheable.
Status Codes
Status classes tell the client where to look:
| Class | Meaning | Examples |
|---|---|---|
| 1xx | Informational | 100 Continue, 101 Switching Protocols |
| 2xx | Success | 200 OK, 201 Created, 204 No Content |
| 3xx | Redirect | 301/308 Moved, 304 Not Modified |
| 4xx | Client error | 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 429 Too Many Requests |
| 5xx | Server error | 500 Internal, 502 Bad Gateway, 503 Unavailable, 504 Gateway Timeout |
The ones that matter operationally: 304 (conditional GET — the cache says “not changed”); 429 (rate-limited — the client must back off); 502/504 (a proxy/load balancer lost contact with the origin — usually your app, not the client). Never return a 5xx for a bad client input, and never a 4xx for a server fault — the class is the contract.
Headers, Cookies, and Caching
Headers carry the negotiation: Content-Type, Authorization, Accept, Content-Length, Cache-Control, ETag, Set-Cookie, Location, Retry-After. The caching pair deserves deep understanding:
Cache-Control—max-age=3600(public caches may reuse for an hour),no-cache(revalidate before reuse),no-store(never store). This is how CDNs know what to cache.ETag— an opaque fingerprint of the response; the client sendsIf-None-Match: <etag>, and the server answers 304 Not Modified if unchanged, saving bandwidth.
Cookies (Set-Cookie/Cookie) are the mechanism browsers use to carry session state; they are per-domain, so cross-site APIs must use explicit auth headers instead. Cookies without Secure/HttpOnly/SameSite are a security hazard — those flags are the difference between a session you control and one an attacker can steal (XSS/CSRF, covered in AppSec).
HTTPS and the TLS Handshake
HTTPS is HTTP running inside TLS (Transport Layer Security), which provides:
- Confidentiality — the payload is encrypted.
- Integrity — tampering is detected.
- Authentication — you’re talking to the server whose certificate you trust.
The TLS handshake (in simplified form):
- Client → Server: supported cipher suites + a random nonce.
- Server → Client: its certificate (public key, signed by a CA the client trusts) + its nonce.
- Client verifies the cert chain, generates a pre-master secret, encrypts it with the server’s public key.
- Both sides derive the same session keys from the secrets and switch to symmetric encryption.
- Each side sends a “Finished” message; application data begins.
That one-time asymmetric dance (public/private key) pays for fast symmetric encryption for the rest of the session. TLS 1.3 cuts the handshake to one round trip (and keeps it private with ECDHE key exchange). For systems work, the operational consequences matter most: certificates expire (Let's Encrypt automation exists precisely because manual renewal fails), and a broken cert chain is the #1 “why is HTTPS broken” cause. The dedicated HTTPS, TLS & Certificates topic goes deeper — the full 1-RTT handshake, ECDHE forward secrecy, the certificate chain of trust, and the 0-RTT replay tradeoff.
HTTP Versions
| Version | Transport | Key feature |
|---|---|---|
| HTTP/1.1 | TCP | Persistent connections, pipelining |
| HTTP/2 | TCP | Multiplexed streams, header compression, server push |
| HTTP/3 | UDP (QUIC) | No head-of-line blocking, faster handshake, connection migration |
HTTP/2 fixes HTTP/1.1’s “one request at a time per connection” problem by multiplexing many streams over one TCP connection — but TCP’s head-of-line blocking still stalls the whole multiplex when a segment drops. HTTP/3 rebuilds on QUIC over UDP so one stream’s loss doesn’t block the others. The practical rule: use HTTP/2/3 for web traffic; the request/response semantics don’t change, only the plumbing.
REST: Resource Modeling
REST is an architectural style (not a protocol) built on HTTP’s own primitives:
- Everything is a resource identified by a URL:
/orders/42. - Operations are the HTTP methods:
GET /orders/42,DELETE /orders/42,POST /orders,PUT /orders/42. - Responses are self-describing (
Content-Type, links). - Stateless — every request is independently complete.
When people say “a RESTful API,” they mean: URL nouns + HTTP verbs + statelessness + status-code semantics. The common failure is modeling actions as nouns (/getOrder) or stuffing state into the URL — if you follow the method table above, you’re most of the way to a clean API.
Practice Trajectory
curl -i https://example.comand read every header; classify each line by layer (TCP handshake → TLS → HTTP).- Fetch a URL twice with
curl -Iand note theCache-Control/ETag; then sendIf-None-Matchand observe a 304. - Start
python3 -m http.serverandcurlit — identify request line, headers, and status line by eye. curl -X POSTwith a body to a test endpoint; repeat it twice and reason about why POST isn’t idempotent but PUT is.- Use
openssl s_client -connect example.com:443to dump the certificate chain — count the CAs and note the expiry date.
When It’s the Right Tool
| Situation | Takeaway |
|---|---|
| Any web service/API | HTTP is the contract — methods, status, caching |
| Building a public API | REST + idempotency + correct status classes |
| Debugging “site down” | The 5xx class points at the origin, not the client |
| Performance | Caching headers + HTTP/2/3 are the first levers |
| Security | HTTPS everywhere; cookies need their security flags |