Networking
HTTP Request
Method, path, headers, body — the handshake of the web
An HTTP request is the message a client sends to a server to fetch or modify a resource. It's a method (GET, POST, etc.), a path, a set of headers, and an optional body — over a TCP+TLS connection. Every page load, every API call, every form submission is one or more HTTP requests. The protocol's evolution from text-based HTTP/1.1 to binary HTTP/3 over QUIC is the story of optimizing for the modern web.
- Common methodsGET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS
- Status code classes1xx info, 2xx success, 3xx redirect, 4xx client err, 5xx server err
- HTTP/1.1 framingPlain text, one request per connection (or pipelined)
- HTTP/2 framingBinary, multiplexed streams over one connection
- HTTP/3 transportQUIC (UDP-based, 1-RTT incl. TLS)
- Default ports80 (HTTP), 443 (HTTPS)
Interactive visualization
Press play, or step through manually. The visualization is yours to drive — try it before reading on.
Watch the 60-second explainer
A condensed visual walkthrough — narrated, captioned, under a minute.
Anatomy of an HTTP request
A raw HTTP/1.1 request looks like this on the wire:
GET /api/users/42 HTTP/1.1
Host: api.example.com
User-Agent: curl/8.4.0
Accept: application/json
Authorization: Bearer eyJhbGc...
Four parts:
- Request line. Method (GET), path (/api/users/42), HTTP version (HTTP/1.1).
- Headers. Key-value metadata. Host is mandatory in HTTP/1.1 (lets one IP host multiple domains).
- Empty line. Separates headers from body.
- Body. Optional. Empty for GET, present for POST/PUT/PATCH.
The response looks similar — first line is HTTP/1.1 200 OK, then headers, then body.
HTTP methods and their semantics
| Method | Purpose | Idempotent | Safe | Has body? |
|---|---|---|---|---|
| GET | Retrieve resource | Yes | Yes | Usually no |
| HEAD | Like GET, no body in response | Yes | Yes | No |
| POST | Submit data, create resource | No | No | Yes |
| PUT | Replace resource at URL | Yes | No | Yes |
| PATCH | Partial update of resource | Should be (often isn't) | No | Yes |
| DELETE | Remove resource at URL | Yes | No | Usually no |
| OPTIONS | Discover allowed methods (also CORS pre-flight) | Yes | Yes | No |
Safe = doesn't modify server state. Idempotent = repeating the request has the same effect as one. These properties are the foundation of HTTP semantics — proxies, caches, retries, and load balancers all rely on them.
Status code summary
| Range | Class | Common members |
|---|---|---|
| 1xx | Informational (rare) | 100 Continue, 101 Switching Protocols (WebSocket upgrade) |
| 2xx | Success | 200 OK, 201 Created, 204 No Content |
| 3xx | Redirection | 301 Moved Permanently, 302 Found (temp), 304 Not Modified, 307/308 (preserve method) |
| 4xx | Client error | 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 422 Unprocessable Entity, 429 Too Many Requests |
| 5xx | Server error | 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable, 504 Gateway Timeout |
Common confusion: 401 vs 403. 401 means "you didn't authenticate" (no credentials, expired token). 403 means "you authenticated but you're not allowed" (correct user, wrong permissions). Use them right and clients can react appropriately.
HTTP/1.1 vs HTTP/2 vs HTTP/3
| HTTP/1.1 | HTTP/2 | HTTP/3 | |
|---|---|---|---|
| Released | 1997 | 2015 | 2022 |
| Transport | TCP | TCP | QUIC (UDP) |
| Framing | Plain text | Binary | Binary |
| Connection setup | 1 RTT (TCP) + 1-2 RTT TLS = 2-3 RTT | 2-3 RTT | 1 RTT (combined) |
| Multiplexing | One request per conn (or pipelined) | Many streams per conn | Many streams per conn (no head-of-line blocking) |
| Header compression | None | HPACK | QPACK |
| Connection migration | No (TCP tied to IP) | No | Yes — survives IP changes |
| Adoption (2025) | ~30% of traffic | ~50% | ~25% and growing |
Each version is a strict superset of the previous in terms of capability — clients negotiate the highest mutually supported version. HTTP/3 over QUIC is rapidly displacing HTTP/2 for major sites because of mobile-network friendliness (connection migration).
JavaScript: making HTTP requests with fetch
// GET — retrieve a resource
const response = await fetch('https://api.example.com/users/42', {
headers: { 'Accept': 'application/json' }
});
if (!response.ok) throw new Error(`HTTP ${response.status}`);
const user = await response.json();
// POST — create a resource
const created = await fetch('https://api.example.com/users', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({ name: 'Alice', email: '[email protected]' })
});
// DELETE — remove a resource
await fetch('https://api.example.com/users/42', { method: 'DELETE' });
// AbortController — cancel an in-flight request
const ctrl = new AbortController();
fetch(url, { signal: ctrl.signal });
ctrl.abort(); // cancels above
Python: requests library
import requests
# GET with query params
r = requests.get('https://api.example.com/users', params={'page': 1, 'per_page': 50})
r.raise_for_status() # throws on 4xx/5xx
data = r.json()
# POST JSON
r = requests.post(
'https://api.example.com/users',
json={'name': 'Alice', 'email': '[email protected]'},
headers={'Authorization': f'Bearer {token}'},
timeout=10,
)
# Streaming download
with requests.get(url, stream=True) as r:
for chunk in r.iter_content(chunk_size=8192):
process(chunk)
Connection management — keep-alive, pipelining, multiplexing
- Keep-alive (HTTP/1.1). The connection stays open after the response — subsequent requests reuse it, avoiding TCP+TLS handshake overhead. Default behavior in HTTP/1.1; clients send
Connection: keep-alive, servers respect it. - Pipelining (HTTP/1.1). Send multiple requests without waiting for previous responses. Theoretically faster, in practice broken by buggy proxies. Most browsers disable it.
- Multiplexing (HTTP/2+). Many requests/responses interleaved on one connection. No head-of-line blocking at the application layer. The big perf win of HTTP/2.
- Connection pooling (clients). Reuse connections across many requests. Default behavior in modern HTTP clients (axios, fetch, requests). Avoids handshake costs.
Common HTTP issues
- CORS errors in the browser. The server didn't include
Access-Control-Allow-Originfor the requesting origin. Fix on the server side, not the client. The browser's CORS error appears in DevTools but the actual request did reach the server — it's the response that's being blocked from JavaScript. - 302 vs 307 vs 308 redirects. 302 may rewrite POST → GET (legacy). 307 preserves the method (POST stays POST). 308 is a permanent 307. Use 308 for permanent redirects of POST endpoints; use 301 for permanent GET redirects.
- Forgetting Content-Type on POST. Server may default to
application/x-www-form-urlencodedand parse JSON as form data, leading to silent corruption. Always setContent-Type: application/jsonwhen sending JSON. - Mixing path and query parameter encoding. URL-encode special characters in query strings (
?q=hello%20world); URL-encode path components separately. Manual concatenation invariably breaks on slashes, quotes, or unicode. - Retrying non-idempotent requests. Auto-retry on network failure is fine for GET/PUT/DELETE; not for POST. Use idempotency keys (custom
Idempotency-Keyheader, server stores result keyed on the value) to make POST safely retryable. Stripe popularized this pattern. - Trailing slashes inconsistency.
/fooand/foo/are different URLs to most servers. Pick a convention (always trailing or never) and redirect to canonical form. Mixing breaks caching and SEO.
Frequently asked questions
What's the difference between GET and POST?
GET retrieves a resource — should be idempotent (running it many times has the same effect as once), parameters in the URL, body usually empty, cacheable, safe to retry. POST submits data to be processed — not idempotent (creates a new resource each call), parameters in the body, not safely cacheable, retrying might double-charge a credit card. The HTTP spec mandates these semantics; servers and proxies rely on them.
What's idempotency?
A request is idempotent if running it multiple times has the same effect as running it once. GET, PUT, DELETE, HEAD, OPTIONS are idempotent by spec. POST is not. Idempotent requests are safe to retry on network failure — a retried PUT just re-sets the same value; a retried POST might create a duplicate. Critical for reliability in distributed systems.
How is HTTP/2 different from HTTP/1.1?
HTTP/2 uses binary framing over a single TCP connection that multiplexes many simultaneous request-response streams. HTTP/1.1 uses text and processes one request per connection at a time (or with pipelining, in strict order). HTTP/2 added header compression (HPACK), server push (controversial, mostly removed in HTTP/3), and stream prioritization. Practical effect — a page with 100 sub-resources loads ~30% faster on HTTP/2.
What's HTTP/3 and why is it over UDP?
HTTP/3 uses QUIC, a transport protocol that runs on UDP and combines connection establishment + TLS into one handshake. The big wins: 1-RTT total connection setup (vs 2-3 for HTTP/2 over TLS over TCP), connection migration when your IP changes (mobile networks), and head-of-line-blocking-free multiplexing (one slow stream doesn't block others). UDP is the substrate because TCP's middlebox interference makes it impossible to deploy TCP changes broadly.
What's the difference between status codes 200, 301, 404, and 500?
200 OK — request succeeded, body has the response. 301 Moved Permanently — resource is now at a different URL (in the Location header); browsers and search engines remember this. 404 Not Found — the URL doesn't exist on this server. 500 Internal Server Error — server encountered an error processing the request (often a code bug). Other notable codes: 401 Unauthorized (no auth), 403 Forbidden (auth but not permitted), 429 Too Many Requests (rate limited), 503 Service Unavailable (server overloaded).
What does "Cache-Control" do?
Tells the client and intermediate caches how to handle the response. <code>Cache-Control: max-age=3600</code> says "fresh for 1 hour, can serve from cache without re-fetching." <code>no-store</code> says "don't cache this at all" (logout pages, sensitive data). <code>private</code> means "browsers can cache but shared CDN proxies cannot." <code>immutable</code> tells browsers to never re-validate during the cache lifetime — used for fingerprinted asset URLs.
What's CORS and why do I keep hitting it?
Cross-Origin Resource Sharing — the browser's enforcement of "JavaScript on origin A can't read responses from origin B by default." The server has to explicitly allow it via <code>Access-Control-Allow-Origin</code>. Pre-flight OPTIONS requests check this before the actual request. CORS protects users from malicious scripts reading authenticated responses on other sites. The error you see in DevTools is the browser blocking the response — the request did go through; it's just that JS can't read the result.