Skip to content

Anatomy of an HTTP Message

Designing an API well starts with seeing exactly what goes over the wire. An HTTP exchange is just text: a request from the client and a response from the server, each with the same three parts.

A request has a start line (method + target + HTTP version), a set of headers, and an optional body:

POST /articles HTTP/1.1
Host: api.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGci...
Accept: application/json
{ "title": "Designing APIs", "body": "..." }

The method (POST) says what to do; the target (/articles) says to what; the headers carry metadata (content type, auth, what the client accepts); the body carries the payload.

The response mirrors that shape: a status line (version + status code + reason), headers, and an optional body:

HTTP/1.1 201 Created
Content-Type: application/json
Location: /articles/42
{ "id": 42, "title": "Designing APIs" }
flowchart LR
  subgraph Request
    A[Start line: method + path] --> B[Headers] --> C[Body optional]
  end
  subgraph Response
    D[Status line: code + reason] --> E[Headers] --> F[Body optional]
  end
  Request -->|over HTTP| Response
Request and response share the same three-part structure

Server frameworks hide most of this, but it helps to construct a response by hand once. This snippet builds the headers and JSON body for a “created” response and logs them:

JavaScript
Advantages (HTTP Structure)Trade-offs
A universal standard — every client knows how to read a request/responseMore verbose than a binary protocol like gRPC
Text-based — human-readable, easy to debug with curlHeader overhead on every request — HTTP/2 mitigates this with header compression
Stateless — every request carries all the information it needsAuth has to be sent on every request, unlike session-based auth
Cacheable — browsers and CDNs can cache based on headersMisconfigured caching can leave clients seeing stale data

Putting sensitive data in the URL instead of a header Symptom:

  • Sending a token in the query string: GET /users?token=abc123
  • The token ends up in logs and browser history
  • Always use Authorization: Bearer <token> in a header

Not setting Content-Type Symptom:

  • Sending a JSON body without Content-Type: application/json
  • The server parses the body wrong, or rejects the request
  • Set Content-Type on every request that has a body

💡 Real-world examples

GitHub API:

  • Recommends Accept: application/vnd.github+json on every request
  • Tells the server the client wants the latest JSON response shape

Stripe API:

  • Uses an Idempotency-Key header on sensitive POST requests
  • Prevents a duplicate charge when the client retries after a network error

Sources: GitHub REST API media types, Stripe idempotent requests

Which part of an HTTP request says WHAT operation the client wants?
After creating a resource, which response header conventionally points to it?
What does the Content-Type header on a response tell the client?