HTTP Methods, Status Codes & Headers

beginner http status-codes methods headers rest

HTTP (HyperText Transfer Protocol) is the foundation of how the web works. Every time we load a page, submit a form, or call an API, we’re using HTTP. It’s a request-response protocol — the client sends a request, the server sends back a response.

Anatomy of an HTTP Request

Every HTTP request has these parts:

GET /api/users?page=1 HTTP/1.1
Host: api.example.com
Accept: application/json
Authorization: Bearer eyJhbGciOi...
  • Method — What we want to do (GET, POST, etc.)
  • URL/Path — The resource we’re targeting (/api/users?page=1)
  • Headers — Metadata about the request (who we are, what we accept, auth tokens)
  • Body — Optional data payload (for POST, PUT, PATCH)

And every HTTP response looks like this:

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: max-age=3600

{"users": [{"id": 1, "name": "Manish"}]}
  • Status code — Did it work? (200, 404, 500, etc.)
  • Headers — Metadata about the response
  • Body — The actual data

HTTP Methods

These tell the server what operation we want to perform:

GET — Retrieve data. Should never modify anything on the server. This is what happens when we load a page or fetch data from an API.

POST — Create a new resource. The data goes in the request body. Submitting a form, creating a user, uploading a file.

PUT — Replace a resource completely. We send the entire updated object. If the resource doesn’t exist, some APIs create it.

PATCH — Partially update a resource. We only send the fields that changed. More efficient than PUT when we’re only changing one field.

DELETE — Remove a resource.

# GET — fetch all users
curl https://api.example.com/users

# POST — create a new user
curl -X POST https://api.example.com/users \
  -H "Content-Type: application/json" \
  -d '{"name": "Manish", "email": "hi@pman47.cc"}'

# PATCH — update just the email
curl -X PATCH https://api.example.com/users/1 \
  -H "Content-Type: application/json" \
  -d '{"email": "new@pman47.cc"}'

# DELETE — remove a user
curl -X DELETE https://api.example.com/users/1

Idempotency and Safe Methods

Two important concepts that come up in interviews:

Safe methods — Don’t modify anything on the server. GET and HEAD are safe. Calling them 100 times has the same effect as calling them 0 times.

Idempotent methods — Calling them once has the same effect as calling them multiple times. GET, PUT, DELETE are idempotent. If we send the same PUT request 5 times, the result is the same as sending it once.

POST is neither safe nor idempotent. Sending the same POST twice might create two resources. That’s why we see “Don’t click submit twice” warnings on payment forms.

Status Codes

Status codes are grouped by their first digit:

1xx — Informational

Rarely seen directly. 100 Continue means “keep sending the request body.”

2xx — Success

  • 200 OK — The request worked. The most common one.
  • 201 Created — A new resource was created (common after POST).
  • 204 No Content — Success, but nothing to send back (common after DELETE).

3xx — Redirection

  • 301 Moved Permanently — The resource has a new URL forever. Search engines update their index.
  • 302 Found — Temporary redirect. The old URL is still valid.
  • 304 Not Modified — The cached version is still good, no need to download again.

4xx — Client Error (our fault)

  • 400 Bad Request — The server can’t understand what we sent (malformed JSON, missing fields).
  • 401 Unauthorized — We’re not authenticated. “Who are you? Show me your ID.”
  • 403 Forbidden — We’re authenticated but don’t have permission. “I know who you are, but you can’t do that.”
  • 404 Not Found — The resource doesn’t exist.
  • 409 Conflict — The request conflicts with the current state (e.g., duplicate email on signup).
  • 429 Too Many Requests — We’re being rate-limited. Slow down.

5xx — Server Error (their fault)

  • 500 Internal Server Error — Something broke on the server. The catch-all error.
  • 502 Bad Gateway — The server (acting as a proxy) got an invalid response from the upstream server. Common with Nginx when the app behind it crashes.
  • 503 Service Unavailable — The server is overloaded or down for maintenance.

Common Headers

Headers carry metadata. Here are the ones we deal with most:

Request headers:

  • Content-Type: application/json — Tells the server what format the body is in
  • Accept: application/json — Tells the server what format we want back
  • Authorization: Bearer <token> — Authentication token (JWT, API key)
  • Cookie: session=abc123 — Sends cookies to the server

Response headers:

  • Content-Type: application/json — Format of the response body
  • Set-Cookie: session=abc123; HttpOnly; Secure — Tells the browser to store a cookie
  • Cache-Control: max-age=3600 — How long the browser can cache this response
  • X-RateLimit-Remaining: 42 — How many API calls we have left
# See all response headers with curl
curl -I https://api.github.com
# -I sends a HEAD request (headers only, no body)

# See request AND response headers
curl -v https://api.github.com 2>&1 | head -30
# Lines starting with > are request headers
# Lines starting with < are response headers

A Real curl Example

Here’s a complete request-response cycle we can actually run:

# Make a POST request and see the full exchange
curl -v -X POST https://httpbin.org/post \
  -H "Content-Type: application/json" \
  -H "X-Custom-Header: hello" \
  -d '{"name": "Manish"}'

# Response includes:
# HTTP/2 200
# content-type: application/json
# {
#   "headers": { "Content-Type": "application/json", "X-Custom-Header": "hello" },
#   "json": { "name": "Manish" },
#   "url": "https://httpbin.org/post"
# }

httpbin.org is great for testing — it echoes back whatever we send, so we can see exactly how our request looks from the server’s perspective.

In simple language, HTTP is a request-response protocol where the method says what we want to do, the status code says what happened, and headers carry the metadata that makes it all work together.

References