Last 30 Days
No notifications
HTTP (HyperText Transfer Protocol) is how clients (browsers, apps) communicate with servers.
Client ββββ Request ββββ> Server
(GET /api/users)
Client <βββ Response ββββ Server
(200 OK + JSON data)| Method | Purpose | Body? | Idempotent? | ||||
| GET | Read data | β | β | ||||
| POST | Create data | β | β | ||||
| PUT | Replace data | β | β | ||||
| PATCH | Partial update | β | β | ||||
| DELETE | Remove data | β | β | Status Codes | Range | Meaning | Common Codes |
| 1xx | Informational | 101 Switching Protocols | |||||
| 2xx | Success | 200 OK, 201 Created, 204 No Content | |||||
| 3xx | Redirect | 301 Moved, 304 Not Modified | |||||
| 4xx | Client Error | 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found | |||||
| 5xx | Server Error | 500 Internal Server Error, 503 Service Unavailable |
Every time you load a webpage, hit "refresh", or tap a button in an app, your computer sends an HTTP request to a server, and the server sends back an HTTP response. That conversation β plain text over a network socket β is the entire web.
HTTP is stateless: each request stands alone. The server doesn't remember you between requests. That's why we invented cookies, sessions, and tokens.
GET /api/users?page=1 HTTP/1.1 β method, path, version
Host: api.example.com β headers (key: value)
Authorization: Bearer eyJhbG...
Accept: application/json
β blank line ends headersA POST/PUT also has a body below that blank line:
POST /api/users HTTP/1.1
Content-Type: application/json
Content-Length: 45{ "name": "Asha", "email": "a@b.com" }
HTTP/1.1 200 OK β status
Content-Type: application/json
Content-Length: 32{ "users": [{ "id": 1 }] }
The status code tells you what happened. The body carries the data.
| Method | Use it for | Has body? | Idempotent? | ||||
| GET | Read a resource | no | yes | ||||
| POST | Create something / submit | yes | no | ||||
| PUT | Replace a resource fully | yes | yes | ||||
| PATCH | Update part of a resource | yes | usually yes | ||||
| DELETE | Remove a resource | usually no | yes | *Idempotent* = doing it 5 times has the same effect as doing it once. POST creates a new row each call β not idempotent. Status Codes You'll See Daily | Range | Meaning | Common ones |
| 1xx | Informational | 101 Switching Protocols (WebSocket) | |||||
| 2xx | Success | 200 OK, 201 Created, 204 No Content | |||||
| 3xx | Redirect | 301 Moved Permanently, 304 Not Modified | |||||
| 4xx | Client error | 400 Bad Request, 401 Unauthorized, 403 Forbidden, 404 Not Found, 409 Conflict, 422 Unprocessable, 429 Too Many Requests | |||||
| 5xx | Server error | 500 Internal Server Error, 502 Bad Gateway, 503 Service Unavailable |
Memory hook: 4xx = *you* messed up. 5xx = *server* messed up.
https://api.example.com:443/users/42?include=posts#section
\___/ \_____________/ \_/ \_____/ \___________/ \_____/
scheme host port path query string hash/users/42)?include=posts&page=1) β you read these on the server1. Putting a body on a GET request. Most servers and proxies will silently drop it. Use the query string.
2. 200 OK for everything, including failures. Pick a real status code so clients can react properly.
3. POST for reads because "GET feels weird". Breaks caching, breaks tooling.
4. Missing or wrong Content-Type. The server tries to parse {...} as a form and you get undefined everywhere.
5. Trusting headers like X-User-Id from the client. Anyone can set those. Authenticate with cryptographically-signed tokens.
| Header | Purpose |
Content-Type | What the body actually is (application/json, text/html, multipart/form-data) |
Accept | What the *client* would like back |
Authorization | Credentials (Bearer , Basic ) |
Cookie / Set-Cookie | Browser-managed key-value store |
Cache-Control | How long this response can be cached |
ETag / If-None-Match | Conditional requests for cheap revalidation |
Location | Where a 201 / 3xx is pointing |
Access-Control-Allow-Origin | CORS β who can call you from a browser |
# JSON β default for APIs
Content-Type: application/json
{ "name": "Asha" }# URL-encoded form β classic <form> submit
Content-Type: application/x-www-form-urlencoded
name=Asha&email=a%40b.com
# Multipart β file uploads
Content-Type: multipart/form-data; boundary=----XYZ
------XYZ
Content-Disposition: form-data; name="avatar"; filename="me.png"
Content-Type: image/png
<binary bytes>
------XYZ--
Your server has to parse the right format β in Express that's express.json(), express.urlencoded(), or multer for files.
HTTPS is HTTP wrapped in TLS. The server presents a certificate, the client verifies it against trusted Certificate Authorities, and an encrypted tunnel is established. Inside the tunnel, it's regular HTTP β same methods, same headers. Modern browsers refuse most features over plain HTTP. In production, always HTTPS (free certs from Let's Encrypt or your platform).
Browsers block JavaScript on yourapp.com from calling api.other.com *unless* the API responds with the right CORS headers:
Access-Control-Allow-Origin: https://yourapp.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Allow-Credentials: trueFor non-simple requests, the browser first sends a preflight OPTIONS request. Servers must answer it. The cors npm package handles all of this for you.
Set-Cookie: token=; HttpOnly; Secure; SameSite=Lax is a popular modern combo.Cache-Control: public, max-age=3600 β cache anywhere for 1 h
Cache-Control: private, no-store β never cache (login pages)
ETag: "v3" β fingerprint of the response
If-None-Match: "v3" β client says "I have v3"
β server replies 304 Not Modified, no bodyGood caching is the cheapest performance upgrade you'll ever ship.
A payment POST that retries due to a network blip must not charge twice. Solution: client sends a unique Idempotency-Key header. The server stores the result against that key and replays it for any retries with the same key. Used by Stripe, Square, etc.
Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alivedata: {"msg":"hi"}\n\n
data: {"msg":"again"}\n\n
An open HTTP response that the server keeps writing to. Perfect for live token streaming (LLM chat), notifications, dashboards β simpler than WebSockets when you only need serverβclient.
WebSockets start as an HTTP request with Upgrade: websocket, then drop into a full-duplex binary protocol. Use them when you need two-way real-time (chat, multiplayer, collaborative editing). Otherwise prefer plain HTTP β easier to cache, log, and debug.
1. Open Chrome DevTools β Network tab on any site. Click a request and identify the method, status code, and 3 headers you recognise.
2. Use curl -v https://httpbin.org/get and read the raw request/response.
3. With curl, send a JSON POST to https://httpbin.org/post with -H 'Content-Type: application/json' -d '{"a":1}' and inspect what the server saw.
4. Force a 304 by hitting any cacheable URL twice with curl --etag-save / --etag-compare.