HTTP Requests and Responses

Every exchange on the web is a pair of messages: the client sends an HTTP request and the server sends back an HTTP response. The request says what the client wants: a method such as POST, a target such as /api/v1/tickets, headers and, when there is data to send, a body. The response says what happened: a status code such as 201, headers and usually a body.

Both are structured text you can read line by line. Submit a support form and the request may carry {"subject": "Late delivery", "orderId": 9021}, while the response answers 201 Created with the new ticket in its body.

Why it matters

The screen shows only what the front end chose to show: "Saved" can appear although the server rejected the data. The request and the response record what really passed between client and server, so they show whether a defect is in the client, the server or both, and who must fix it.

The request: method, target, headers, body

In HTTP/1.1 (RFC 9112) a request has a fixed layout: a request line, one header field per line, an empty line, then an optional body. The request line and every header line end with a carriage return and a line feed (CRLF).

  • Method: the first word names the action, such as GET to read or POST to submit data. The next lesson covers each method.
  • Request target: usually the path plus an optional query string, here /api/v1/tickets?lang=ar; the host name goes separately, in the Host header.
  • Protocol version: HTTP/1.1 closes the request line.
  • Header fields: Name: value lines such as Host (the host the request is for), Content-Type and Content-Length (the body's format and size), Accept and Authorization. An HTTP/1.1 request must carry exactly one Host header, or the server must answer 400 Bad Request.
  • Body: everything after the empty line, present only when there is data to send.
POST /api/v1/tickets?lang=ar HTTP/1.1
Host: support.example.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer eyJhbGciOi...
Content-Length: 45

{"subject": "Late delivery", "orderId": 9021}

Try it: API Request Builder

The response: status line, headers, body

The server answers with the same layout; only the first line differs. The status line holds the protocol version, a three-digit status code and a reason phrase.

  • Status code: the outcome as a number. Clients act on it, so it is what you assert on. The first digit gives the class: 1xx informational, 2xx success, 3xx redirection, 4xx a problem with the request, 5xx an error on the server.
  • Reason phrase: text for humans, such as Created. RFC 9112 tells clients to ignore it, servers may word it differently, and HTTP/2 does not carry it, so never assert on it.
  • Header fields: facts about the response, such as the body's format and size, the Location of a new resource, Set-Cookie and Cache-Control.
  • Body: the requested data, a description of an error, or nothing.
A request and its response share one frame of start line, header fields, empty line and optional body; only the start line differs: method, target and version against version, status code and reason phrase.

Request

Part 1:POST Part 2:/api/v1/tickets?lang=ar Part 3:HTTP/1.1
Part 6:Host: support.example.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer eyJhbGciOi...
Content-Length: 45
Part 7:
Part 8:{"subject": "Late delivery", "orderId": 9021}

Response

Part 3:HTTP/1.1 Part 4:201 Part 5:Created
Part 6:Date: Thu, 01 Oct 2026 09:30:00 GMT
Content-Type: application/json
Content-Length: 62
Location: /api/v1/tickets/T-1047
Part 7:
Part 8:{"id": "T-1047", "status": "open", "subject": "Late delivery"}
  • 1–3Request line
  • 3–5Status line
  • 6–8The same in both messages
  1. 1MethodThe action requested; POST submits data. Each method has its own rules.
  2. 2Request targetThe path plus an optional query. The host name travels in the Host header.
  3. 3Protocol versionOpens the status line and closes the request line. HTTP/2 and HTTP/3 put no version in each message; the connection sets it.
  4. 4Status codeThree digits; the first gives the class. Clients act on it, so assert on it.
  5. 5Reason phraseText for humans: clients ignore it and HTTP/2 does not carry it. Never assert on it.
  6. 6Header fieldsOne Name: value per line. Names are case-insensitive, and the order of different fields carries no meaning.
  7. 7Empty lineEnds the header section: everything after it is the body.
  8. 8BodyOptional in both messages. Content-Type gives its format, Content-Length its size in bytes.
Text version
HTTP/1.1 201 Created
Date: Thu, 01 Oct 2026 09:30:00 GMT
Content-Type: application/json
Content-Length: 62
Location: /api/v1/tickets/T-1047

{"id": "T-1047", "status": "open", "subject": "Late delivery"}

Try it: HTTP Status Code Lookup

Request bodies and response bodies

RFC 9110 calls the body the content; tools say body or payload. Content-Type names its format as a media type, such as application/json for JSON, the text format many APIs use, with names and values inside braces. Content-Length gives its size in bytes, unless an HTTP/1.1 sender uses Transfer-Encoding: chunked and sends the body in pieces.

  • Request bodies carry the data you create or change, so POST, PUT and PATCH requests usually have one. GET, HEAD and DELETE requests usually do not: RFC 9110 gives content in them no generally defined meaning.
  • Response bodies carry what you asked for, such as JSON, HTML or a file, or a description of an error; RFC 9457 defines a standard error format, application/problem+json (sample below).
  • Some responses never have a body: any response to HEAD, and every 1xx, 204 No Content and 304 Not Modified response.
  • Content-Length counts bytes, not characters. In UTF-8, the usual encoding that turns web text into bytes, each English letter, digit and punctuation mark takes one byte, each Arabic letter two and each Devanagari character three: {"name": "سارة"} is 16 characters but 20 bytes.
HTTP/1.1 422 Unprocessable Content
Content-Type: application/problem+json
Content-Length: 132

{"type": "https://example.com/probs/missing-field", "title": "Missing field",
 "status": 422, "detail": "subject must not be empty"}

Try it: Content-Type Validator Try it: String Length Counter

Reading raw HTTP

Raw HTTP is the message as text, as in the samples above. In DevTools, the Network panel's Headers tab has a Raw toggle, and the request body is under Payload in Chrome or Request in Firefox. curl -v prefixes the start line and header lines it sent with > and those it received with <. Read in a fixed order:

  • Start line: are the method and target the ones the test intended? In a response, take the status code, not the reason phrase.
  • Headers: names are case-insensitive and the order of different fields carries no meaning, so compare names and values only.
  • The empty line: before it, facts about the message; after it, the body.
  • Cross-checks: the status code against the body, Content-Type against the body's real format, Content-Length against its bytes. Each part can look right alone while the combination is wrong.

Try it: HTTP Headers Parser Try it: HTTP Request Inspector

Worked example: "Saved", but the address is missing

A shopping app says "Saved" after a tester adds a delivery address, but the address list stays empty. The API documentation says the body needs city and postalCode, success returns 201 Created, and a validation error returns 422 with a problem details body. The tester repeats the step with DevTools open and copies the exchange.

  • Request line and headers: as documented; Content-Type matches the JSON body and Content-Length: 42 equals its 42 bytes.
  • Request body: postal_code instead of postalCode. Defect 1, in the app.
  • Status line: 200 OK for a save that failed; the app trusted the 2xx and showed "Saved". Defect 2, in the API: expected 422.
  • Response headers: Content-Type: text/plain for a JSON body. Defect 3, in the API: a client that follows the header may not parse the error.
  • Response body: it names the missing field, which pointed to defect 1. Three defects, two owners, one exchange.
POST /api/v1/customers/58/addresses HTTP/1.1
Host: api.example.com
Content-Type: application/json
Accept: application/json
Authorization: Bearer eyJhbGciOi...
Content-Length: 42

{"city": "Riyadh", "postal_code": "12271"}

HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 53

{"success": false, "error": "postalCode is required"}

Common mistakes

  • Judging a result by the screen or by the status code alone: a 200 OK with an error in its body passes unnoticed.
  • Asserting on the reason phrase, or on the case and order of headers, instead of on status codes and header values.
  • Trusting Content-Type because the body looks right to you: a client that follows the header can still fail to read it.
  • Counting Content-Length in characters: Arabic, Hindi and emoji text takes more bytes than characters in UTF-8.
  • Reporting "the request failed" without the raw exchange, or pasting it with live Authorization and Cookie values instead of masking them.

The QA perspective

Read raw HTTP whenever the screen and the data disagree. Open the Network panel before you reproduce, find the request behind the action, and ask two questions: did the client send what the specification asks for, and did the server answer as the API documentation says?

In the bug report, attach the exchange as text, or as a cURL command plus the response, with tokens masked, and name the wrong part: "Expected 422 with a problem details body; actual 200 OK with Content-Type: text/plain." Ask the API team early for each endpoint's status codes and error format.

Practice exercise

Read two raw exchanges and find what is wrong

Your task

The two exchanges below come from a staging environment. Mark the parts of all four messages, then check: does each status code agree with its body, does Content-Type describe the body, and does Content-Length match the body's size in bytes?

For each problem, write the expected and the actual result and say who must change, the client or the server.

Material
(1)
PUT /api/v1/profile HTTP/1.1
Host: app.example.com
Content-Type: application/json
Authorization: Bearer eyJhbGciOi...
Content-Length: 23

{"displayName": "نورة"}

HTTP/1.1 400 Bad Request
Content-Type: application/json
Content-Length: 70

{"error": "invalid_json", "message": "Request body is not valid JSON"}

(2)
DELETE /api/v1/profile/addresses/3 HTTP/1.1
Host: app.example.com
Authorization: Bearer eyJhbGciOi...

HTTP/1.1 204 No Content
Content-Type: application/json
Content-Length: 30

{"message": "Address deleted"}
Steps
  1. Mark the start line, header fields, empty line and body of each message.
  2. Compare Content-Type with the real format of each body.
  3. Count the bytes of each body with the String Length Counter (UTF-8) and compare them with Content-Length.
  4. Compare each status code with its body, then decide who owns each problem.
Show the model answer
Model answer

Parts: every message has its start line, header fields and the empty line. The DELETE request has no body; the 204 response has bytes after the empty line that are not allowed there (see exchange 2); the other two messages have a body. Content-Type says JSON and every body is JSON, so that check finds nothing.

Exchange (1): the body {"displayName": "نورة"} is 23 characters but 27 bytes, because each Arabic letter takes two bytes in UTF-8. The client declared Content-Length: 23, so the server read only {"displayName": "نور and rightly rejected it as invalid JSON; its 400 response is consistent (70 bytes, as declared). The defect is in the client, which counts characters instead of bytes; a test with an English name would never show it.

Exchange (2): the DELETE request rightly has no body. But a 204 No Content response cannot contain content (RFC 9110), and a server must not send Content-Length with it. Clients treat the response as ending at the empty line, so the message is never shown, and on a reused HTTP/1.1 connection the 30 extra bytes can be misread as the next response. The server must send 204 with nothing after the empty line, or 200 OK with the body.

Frequently asked questions

Do I need to know HTTP/2 and HTTP/3 to read requests and responses?

No. HTTP/2 and HTTP/3 send the same parts (method, target, status code, header fields, body) in binary frames instead of text lines, and RFC 9110 defines their meaning for every version. Tools show them in a readable form; expect lower-case header names, pseudo-headers such as :method and :path, and no reason phrase.

Are body, payload and content the same thing?

In everyday use, yes: the data after the header section. RFC 9110 calls it content; RFC 9112, which defines the HTTP/1.1 text format, calls the bytes on the wire the message body, which differs from the content when a transfer coding such as chunked is applied. Many tools say payload.

Can a GET request have a body?

A message can carry one, but RFC 9110 gives content in a GET request no generally defined meaning, and servers, proxies and caches may ignore or reject it. Send GET input in the query string. If an API documents a GET body, treat it as a risk and test it through every client and proxy in use.

References

  • RFC 9110: HTTP SemanticsIETF · 2022The meaning of HTTP methods, header fields, status codes and content negotiation, independent of the wire version.
  • RFC 9112: HTTP/1.1IETF · 2022The HTTP/1.1 message syntax: request line, header fields, chunked transfer and connection handling.
  • RFC 9457: Problem Details for HTTP APIsIETF · 2023The application/problem+json error format (type, title, status, detail, instance) for machine-readable API errors.
  • Overview of HTTP — MDNMDN Web Docs (Mozilla)How HTTP works: a stateless client–server protocol in which the client sends a request and the server answers with a response message.

Practise with Mutqan tools

Parse raw HTTP request/response headers into a structured table with explanations.

Web

See exactly what your client sends: method, headers, query, body, IP and TLS info.

API Testing

Compose and send HTTP requests (method, URL, headers, auth, body) and inspect the response — a lightweight Postman in your browser.

API Testing

Validate Content-Type headers and check that a response body actually matches its declared media type.

Web

Measure string length in characters, code points, UTF-8 bytes and UTF-16 units.

Formatting

Go deeper

Your progress on this topic

Part of the QA Roadmap