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
GETto read orPOSTto 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 theHostheader. - Protocol version:
HTTP/1.1closes the request line. - Header fields:
Name: valuelines such asHost(the host the request is for),Content-TypeandContent-Length(the body's format and size),AcceptandAuthorization. An HTTP/1.1 request must carry exactly oneHostheader, or the server must answer400 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}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:
1xxinformational,2xxsuccess,3xxredirection,4xxa problem with the request,5xxan 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
Locationof a new resource,Set-CookieandCache-Control. - Body: the requested data, a description of an error, or nothing.
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
- 1MethodThe action requested;
POSTsubmits data. Each method has its own rules. - 2Request targetThe path plus an optional query. The host name travels in the
Hostheader. - 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.
- 4Status codeThree digits; the first gives the class. Clients act on it, so assert on it.
- 5Reason phraseText for humans: clients ignore it and HTTP/2 does not carry it. Never assert on it.
- 6Header fieldsOne
Name: valueper line. Names are case-insensitive, and the order of different fields carries no meaning. - 7Empty lineEnds the header section: everything after it is the body.
- 8BodyOptional in both messages.
Content-Typegives its format,Content-Lengthits 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"}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,PUTandPATCHrequests usually have one.GET,HEADandDELETErequests 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 every1xx,204 No Contentand304 Not Modifiedresponse. Content-Lengthcounts 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-Typeagainst the body's real format,Content-Lengthagainst its bytes. Each part can look right alone while the combination is wrong.
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-Typematches the JSON body andContent-Length: 42equals its 42 bytes. - Request body:
postal_codeinstead ofpostalCode. Defect 1, in the app. - Status line:
200 OKfor a save that failed; the app trusted the2xxand showed "Saved". Defect 2, in the API: expected422. - Response headers:
Content-Type: text/plainfor 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 OKwith 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-Typebecause the body looks right to you: a client that follows the header can still fail to read it. - Counting
Content-Lengthin 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
AuthorizationandCookievalues 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
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.
(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"}- Mark the start line, header fields, empty line and body of each message.
- Compare
Content-Typewith the real format of each body. - Count the bytes of each body with the String Length Counter (UTF-8) and compare them with
Content-Length. - Compare each status code with its body, then decide who owns each problem.
Show the 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 SemanticsThe meaning of HTTP methods, header fields, status codes and content negotiation, independent of the wire version.
- RFC 9112: HTTP/1.1The HTTP/1.1 message syntax: request line, header fields, chunked transfer and connection handling.
- RFC 9457: Problem Details for HTTP APIsThe application/problem+json error format (type, title, status, detail, instance) for machine-readable API errors.
- Overview of HTTP — MDNHow 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.
See exactly what your client sends: method, headers, query, body, IP and TLS info.
Compose and send HTTP requests (method, URL, headers, auth, body) and inspect the response — a lightweight Postman in your browser.
Validate Content-Type headers and check that a response body actually matches its declared media type.
Measure string length in characters, code points, UTF-8 bytes and UTF-16 units.
Look up any HTTP status code with its meaning, RFC reference and when to use it.
Go deeper
Your progress on this topic
Not started
Mark the topic complete when you have studied it and done the exercise.
Sign in to track your progress
Mark topics complete, continue where you left off and unlock specialisation exams.