HTTP: methods, status codes, headers, body

The protocol every API speaks, read as a conversation you can inspect.

5 min read🧭 Programming Foundations

HTTP is the protocol every web API speaks. It is text, it is simpler than its reputation, and you can read a whole exchange by eye — which is exactly what makes it debuggable.

Here is a real one, from this site:

curl -D - -o /dev/null https://code10x.in/api/meplaintext
HTTP/1.1 200 OK
Server: nginx/1.24.0 (Ubuntu)
Content-Type: application/json
Cache-Control: private, no-store
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
 
{"user":null}

Every part of that is something you will set deliberately one day.

The four parts of a request

plaintext
POST /api/orders HTTP/1.1          ← method, path, version
Host: api.example.com              ← headers: metadata about the request
Content-Type: application/json
Authorization: Bearer eyJhbGc…
                                   ← a blank line, then
{"item": "book", "qty": 2}         ← the body

Method — what kind of action. Path — which resource. Headers — everything about the request that is not the request. Body — the data, on the methods that carry one.

A response has the same shape with a status line instead of a request line.

Methods, and the two properties that matter

MethodMeansSafe?Idempotent?
GETread somethingyesyes
POSTcreate something, or "do a thing"nono
PUTreplace something entirelynoyes
PATCHchange part of somethingnousually not
DELETEremove somethingnoyes

Safe means it changes nothing. A GET that creates an order is a bug, and a serious one — browsers, proxies and crawlers all assume they may repeat a GET freely, and some will.

Idempotent means doing it twice has the same effect as doing it once. DELETE twice leaves the thing deleted. PUT twice leaves the same value. POST twice creates two orders — which is why a user double-clicking Submit, or a client retrying after a timeout, is a real and expensive problem. It has a whole lesson to itself later in the course; for now, notice that the property comes from the method's meaning, not from anything the protocol enforces.

Status codes by their first digit

You do not memorise the list. You learn the ranges, and look up the rest.

RangeMeaningThe ones you will use
2xxit worked200 OK, 201 Created, 204 No Content
3xxlook elsewhere301 permanent, 302/308 temporary or method-preserving
4xxthe client got it wrong400, 401, 403, 404, 409, 422, 429
5xxthe server got it wrong500, 502, 503, 504

The 4xx/5xx boundary is the one that matters most and is most often got wrong. It is a statement about whose fault it is, and it drives alerting: a spike in 4xx means clients are sending bad requests, a spike in 5xx means you are broken. Returning 500 for a missing field trains everybody to ignore your alerts.

Two pairs worth separating now:

  • 401 vs 403. 401 is I do not know who you are — sign in. 403 is I know who you are and you may not — signing in again will not help.
  • 404 vs 400. 404 is there is nothing at this path. 400 is the path is fine, what you sent is not.
POST /api/ordersheadersthe bodyserver decidesthe status
known so farwhat and wherebody read?nostatusnone yet

POST /api/orders. Method and path. The method says what KIND of action — and POST is neither safe nor idempotent, which is why sending it twice creates two orders.

1 / 5

Headers carry the interesting metadata

plaintext
Content-Type: application/json      what the body is
Authorization: Bearer eyJhbGc…      who is asking
Accept: application/json            what I would like back
Cache-Control: private, no-store     who may keep a copy, and for how long

Two of those appear in the real response above. Cache-Control: private, no-store on /api/me is deliberate: this response is about you, and no proxy or browser should keep it where another person could be served it.

The response above also carries X-Content-Type-Options and X-Frame-Options — security headers, which exist because a browser will do dangerous things by default unless told not to. They come up properly in the security phase.

It is a request-response protocol, and that shapes everything

The client asks, the server answers, and the exchange is over. The server cannot push you something later on that connection.

Everything that looks like a server-initiated message — a notification, a live update — is built on top of that limitation: the client polls repeatedly, or holds a connection open, or uses a different protocol entirely. Knowing that the base is one-shot explains why "just push it to the client" is never as simple as it sounds.

Try it yourself

Read a real exchange

curl is the tool. -D - prints the response headers; -o /dev/null throws the body away when you only want them.

  1. Fetch any API and read its headers. What Content-Type did it send?
  2. Request a path that does not exist. What status came back?
  3. Find a URL that redirects, and look at the Location header.
  4. Send a POST with a JSON body to an API that expects one, and look at the status.
The commands, and what to notice
bash
curl -s -D - -o /dev/null https://code10x.in/api/me
curl -s -o /dev/null -w '%{http_code}\n' https://code10x.in/no-such-page-here   # 404
curl -s -D - -o /dev/null https://code10x.in//                                  # 308 + Location
curl -s -X POST -H 'Content-Type: application/json' -d '{"a":1}' https://example.com/api

Notice that the 404 came back with a full HTML page in its body. A status code and a body are independent: an error status can carry a helpful explanation, and a 200 can carry a body saying something failed — which is a design mistake, but a common one.

Misconceptions

  • "HTTPS is a different protocol." It is HTTP inside an encrypted tunnel. Same methods, same status codes, same headers — an observer sees which server you contacted and nothing more.
  • "POST is for creating, PUT is for updating." Closer: PUT replaces at a known location and is idempotent; POST is the general "do something" and is not. The difference that matters is idempotency, not the verb's name.
  • "A 200 means it worked." It means the request was handled. A 200 whose body says {"error": "..."} is a real and unfortunate pattern.
Progress is saved on this device and to your account when signed in.