Skip to content
Tapwire

Anatomy of an HTTP request

Method, path, query string, headers and body: what each part means and how to read it when debugging.

8 min read · Updated 24 October 2026

Every HTTP request has the same four parts: a method, a target (path plus optional query string), a set of headers, and an optional body. Once you can read those four parts confidently, most integration bugs become visible within seconds of looking at a captured request.

PATCH /v1/customers/42?expand=address&dry_run=true HTTP/1.1
Host: api.example.com
Authorization: Bearer sk_test_…
Content-Type: application/json
Accept: application/json
Content-Length: 32

{"email":"ada@example.com"}

The method

The method says what the client wants to do. Two properties defined by the HTTP specification matter in practice. A safe method does not change anything on the server. An idempotent method has the same effect whether it is sent once or several times, which matters whenever a request might be retried.

MethodTypical useSafeIdempotent
GETRead a resourceYesYes
HEADSame as GET, but headers onlyYesYes
OPTIONSAsk what is allowed; used for CORS preflightYesYes
POSTCreate something or trigger processingNoNo
PUTReplace a resource entirelyNoYes
PATCHChange part of a resourceNoNot necessarily
DELETERemove a resourceNoYes

Webhooks are almost always POST, which is not idempotent by definition. That is why webhook receivers need their own duplicate detection based on event IDs.

The path and the query string

The path identifies the resource: /v1/customers/42. Everything after the first ? is the query string, a list of name=value pairs separated by &. A few details regularly cause confusion:

  • Encoding. Characters with special meaning must be percent-encoded. A space can appear as %20, and in form-style query strings also as +. An ampersand inside a value must be %26, otherwise it splits the value in two.
  • Repeated keys. ?tag=a&tag=b is valid and means two values. Frameworks disagree on how to expose this: some give you the first value, some the last, some an array. An inspector shows you the raw string so you can see what was actually sent.
  • Empty values. ?debug and ?debug= are different strings, and code that checks for a non-empty value treats them differently.
  • Secrets in URLs. Query strings end up in server logs, proxy logs and browser history. Never put API keys or tokens there if you can use a header instead.

Headers

Headers carry metadata about the request. Names are case-insensitive: Content-Type and content-type are the same header. HTTP/2 and HTTP/3 send all names in lowercase, and many tools, Tapwire included, display them that way.

HeaderWhat it tells you
Content-TypeHow to interpret the body, for example JSON, a form, or binary data
Content-LengthBody size in bytes, when known in advance
AuthorizationCredentials, such as a bearer token or basic auth
User-AgentWhich client or provider sent the request; useful for filtering
AcceptWhich response formats the client can handle
Idempotency-KeyA client-chosen key some APIs use to make retries of POST safe

Providers also add their own headers, commonly prefixed with their name, for the event type, a delivery ID and a signature. Those are usually the headers your code needs most.

The body

The body is a sequence of bytes. The Content-Type header tells the receiver how to decode it, and a mismatch between the two is one of the most common integration bugs. The formats you will meet most often:

  • application/json: structured data. See working with JSON payloads.
  • application/x-www-form-urlencoded: the same name=value&… format as a query string, used by HTML forms and some older webhook providers.
  • multipart/form-data: several parts separated by a boundary string declared in the header, used for file uploads.
  • text/plain or application/octet-stream: raw text or raw bytes with no further structure.

GET and HEAD requests normally have no body. Some clients send one anyway, and many servers ignore it, so do not design an API that depends on it.

What an inspector can and cannot show

A request inspector records the request after the HTTP layer has parsed it. You see the method, the decoded target, every header value and the exact body bytes. A few things are outside its view: TLS details such as client certificates, the exact original order and capitalization of headers (HTTP/2 and the platform normalize them), and anything that happened on the sender's side before the request reached the network, such as a request that failed DNS lookup and was never sent. If a request never appears in the inspector, the problem is upstream of it.