AlgoMaster Logo

HTTP Request/Response Anatomy

High Priority34 min readUpdated August 14, 2026
Listen to this chapter
Unlock Audio

HTTP is the application-layer protocol behind browsers, REST APIs, package registries, webhooks, and many service-to-service calls. Its basic interaction is simple: a client sends a request, and a server sends a response.

What makes HTTP practical is that each message describes itself. The request identifies an operation and a target. The response reports an outcome. Header fields carry metadata, and an optional body carries data such as JSON, HTML, an image, or a compressed file.

Frameworks usually hide this structure behind objects such as Request, Response, or HttpClient. On the network, however, those objects must become a precisely framed sequence of bytes. A missing blank line, an incorrect byte count, or two components disagreeing about where a message ends can break a request or create a serious security problem.

This chapter develops a wire-level mental model of an HTTP exchange. HTTP/1.1 is the clearest version for learning the anatomy because its control information is written as readable lines. The same core ideas—method, target, fields, status, and content—also exist in newer HTTP versions, although their wire encodings differ.

The HTTP Exchange

HTTP assigns roles for each exchange.

A client opens or reuses a suitable connection and sends a request. Browsers are clients, but so are mobile apps, command-line tools, backend services, health checkers, and monitoring agents.

A server receives the request, interprets it, and returns a response. The server might be an application process, a reverse proxy, an API gateway, or another intermediary acting as a server for one connection and a client for the next.

Most exchanges produce one final response. A server can send one or more interim responses before that final response, but the final response is the authoritative outcome of the request.

HTTP is described as stateless because the meaning of a request can be understood independently. Stateless does not mean that every request needs a new network connection, nor does it prevent an application from maintaining user state through cookies, tokens, or server-side storage.

Semantics and Wire Format Are Separate

It helps to separate two parts of HTTP.

HTTP semantics define what a request means: its method, target resource, field values, and content. They also define what a response means: its status, field values, and content.

HTTP message syntax defines how those elements are encoded and framed on a connection.

HTTP/1.1 serializes control information as text-like lines. HTTP/2 and HTTP/3 carry equivalent concepts in binary frames rather than sending a literal textual request line or status line. An application can therefore work with nearly the same logical request even when the negotiated wire format changes.

HTTPS does not introduce a different request or response model. It protects HTTP with TLS. For an HTTP/1.1 HTTPS connection, the endpoints still create and parse the structures shown in this chapter, but observers between them see encrypted TLS records rather than the plaintext HTTP bytes.

The Shape of an HTTP/1.1 Message

Every HTTP/1.1 message has four parts:

The start line and every header field line end with CRLF, the two-byte sequence carriage return followed by line feed:

The empty line after the headers is required. At the byte level, the end of the header section is:

The start line determines whether the message is a request or a response:

Consider this complete HTTP/1.1 exchange:

The rendered code blocks show ordinary line breaks, but a conforming HTTP/1.1 sender places \r\n after each control line. The blank line separates metadata from content. The body begins immediately after it; there is no additional marker saying “body starts here.”

Loading simulation...

Request Anatomy

An HTTP/1.1 request begins with a request line:

In the example:

the three components are:

SP means one space byte, 0x20. A sender should not align components with extra spaces or tabs. Some implementations accept loose whitespace for compatibility, but inconsistent parsing between recipients is dangerous. Generating the strict form avoids ambiguity.

Method

The method is a case-sensitive token that states the purpose of the request. In this chapter, POST simply provides a request that naturally carries content. The method is not part of the path and is not a header field; it occupies the first position in the request line.

HTTP allows extension methods, so a parser should validate the token syntax rather than assume that only a hard-coded list can appear. Whether a server supports a particular method is a separate application decision.

Request Target

The request target identifies what the request is directed at. For a direct request to an origin server, it normally contains the path and optional query:

Suppose the user starts with:

The components serve different purposes:

A direct HTTP/1.1 request normally looks like:

The client does not send the fragment. A fragment such as #recent is interpreted locally by the user agent after it obtains a representation. It is not part of the HTTP request target.

If the URL has no path, the client sends /:

The path is case-sensitive unless the server defines otherwise. The query is separated from the path by ?, but its internal meaning belongs to the target application. Spaces and other characters that cannot appear directly in a URI must be encoded before the request line is created.

HTTP/1.1 supports a few target forms for specialized situations:

Origin form is the common choice for a client talking directly to an origin server. Forward proxies use absolute form, while the other forms serve narrowly defined protocol operations.

Request Header Fields

The request line is followed by zero or more header field lines:

For example:

The field name is Content-Type, and the field value is application/json.

Field names are case-insensitive, so these names identify the same field:

Field values follow the rules of their individual fields and are not universally case-insensitive. A program should normalize names for lookup while preserving and interpreting values according to the relevant field definition.

A sender must not place whitespace between the field name and the colon:

Modern senders also keep each field line on one line. The historical practice of continuing a field value on the next indented line is obsolete and can cause different recipients to interpret the same bytes differently.

In HTTP/1.1, a request must contain exactly one valid Host field. It identifies the target host and optional port:

This matters because one server address can host many domain names. The path /v1/orders alone does not identify which site's resource the client wants.

Header fields can describe the client, provide credentials, express preferences, carry routing information, or describe content. They are metadata about the request; they are not the request body.

The Empty Line

After the final header field, the sender writes one additional CRLF:

That visually blank line is part of the HTTP/1.1 syntax. It ends the header section.

The blank line is still required when the request has no body:

Without it, the receiver cannot know whether another header field is about to arrive.

Request Body

A request can contain a body when its method and field values define a meaning for that content. Common body formats include JSON, form data, protocol buffers, images, and arbitrary binary files.

The body is a sequence of bytes, not necessarily text:

In the example:

the body is 30 bytes when encoded as UTF-8. Therefore:

Content-Length counts only the body bytes. It does not include the request line, headers, blank line, or any bytes from surrounding network protocols.

Character count and byte count are not always equal. The text "₹" is one Unicode character but occupies three bytes in UTF-8. A sender must calculate the encoded byte length, not the number of user-visible characters.

Response Anatomy

An HTTP/1.1 response has the same overall framing as a request, but it starts with a status line:

For example:

Its components are:

HTTP Version

The version identifies the HTTP message syntax used by the sender:

It describes the message being sent; it is not an application version such as v1 in /v1/orders.

Status Code

The status code is a three-digit number that communicates the result of processing the request. Clients use the numeric code for protocol decisions.

The response does not normally repeat the request method or target:

The client associates this response with its request using the connection and protocol state. There is no standard request ID in the HTTP/1.1 status line.

Reason Phrase

The optional reason phrase is human-readable text following the status code:

A client must not depend on its wording. A server could send a different phrase, an empty phrase, or a localized phrase while keeping the same numeric status code. The number carries the standardized meaning.

Response Header Fields

Response fields use the same name: value line syntax as request fields:

They can describe the response, the server, the selected representation, or instructions for handling the result.

The header section again ends with an empty line. A client must finish parsing the complete header section before treating subsequent bytes as content.

Response Body

When a response carries content, its body starts immediately after the empty line:

This body is 36 UTF-8 bytes, matching:

Not every response has a body. Whether content is allowed depends on the request method and response status, not merely on whether bytes happen to be available on the connection. A correct client uses the HTTP framing rules and the request context instead of assuming that every final response contains content.

The Content-Type field describes how to interpret the content. It does not transform the body into text, and it is not a body delimiter. The body can contain any byte value, including bytes that look like CRLF or the end of a header section.

How HTTP/1.1 Finds the End of a Body

The empty line identifies where a body begins, but it does not by itself identify where the body ends. HTTP/1.1 needs an explicit framing rule so that a receiver can separate one message from whatever comes next.

A Known Byte Length

The simplest framing uses Content-Length:

After the empty line, the receiver reads exactly 30 bytes. Byte 31 belongs to something else: perhaps the next message, or perhaps data the server must not have sent.

A short read from the transport does not mean that the body ended early. The receiver continues reading until it collects the declared number of bytes or the connection fails.

Chunked Transfer Coding

When the sender does not know the final length before it begins sending, HTTP/1.1 can encode the body as chunks:

Each chunk begins with its byte length written in hexadecimal. The zero-length chunk marks the end:

The chunk-size lines, their CRLF delimiters, and the final zero chunk are transfer framing. They are not part of the decoded application content.

Chunked messages can also carry trailer fields after the zero chunk and before the final empty line. Trailers are fields computed after content has been sent, so they appear at the end rather than in the initial header section. They are less common than ordinary header fields, but a complete parser must frame them correctly.

Connection Closure

Some HTTP responses are delimited by the server closing the connection. In that case, all body bytes continue until the transport reports end-of-stream.

Closure-based framing makes the connection unusable for another exchange and cannot distinguish a complete response from some kinds of connection failure. Explicit length or chunked framing is therefore much easier to process reliably.

Requests are not normally close-delimited. If a request has neither a valid Content-Length nor transfer coding, its body length is zero.

Framing Must Be Unambiguous

A sender must not attach both Content-Length and Transfer-Encoding to the same message. Conflicting lengths or contradictory framing fields can cause a proxy and an application server to disagree about where one request ends and the next begins.

Robust software does not guess which interpretation the sender intended. It validates framing before processing the message and rejects dangerous ambiguity.

An HTTP Message Is Not a Packet

HTTP defines message boundaries. TCP provides an ordered byte stream. The boundaries of application writes, TCP segments, IP packets, and socket reads are independent.

A client might create one 240-byte request and pass it to a socket with one call. The server could receive it like this:

It could also receive that request together with bytes from a later request:

The reverse is true for responses. One large body can span many network packets, while a small response's headers and body might arrive in one socket read.

This leads to a fundamental implementation rule:

An HTTP parser must buffer incomplete control lines, preserve unused bytes, and read bodies according to HTTP framing. Searching for \r\n\r\n locates the end of the header section, not necessarily the end of the message.

How a Receiver Parses One Message

A simplified HTTP/1.1 receiver follows this process:

  1. Read until it has one complete start line ending in CRLF.
  2. Determine whether the expected start line is a request line or a status line and validate its components.
  3. Read field lines until the empty line, applying limits to line length, field count, and total header size.
  4. Validate fields that affect routing and message framing.
  5. Determine whether a body is permitted and, if so, whether its boundary comes from a byte length, chunked coding, or connection closure.
  6. Read or stream exactly that body while preserving any bytes belonging to the next message.

Request parsing begins with server-side connection context. Response parsing additionally needs the corresponding request context because the request method can affect whether the response is allowed to contain a body.

Real implementations impose size and time limits even where HTTP does not define one universal maximum. Without limits, a peer could send an extremely long start line, an endless header section, or a body too slowly and hold resources indefinitely.

A receiver should parse structural delimiters as bytes. It should not decode the entire message as one Unicode string: the body might be an image, compressed data, or UTF-8 text split in the middle of a multi-byte character. Header parsing and content decoding are separate operations.

Observing an Exchange with curl

curl can expose the logical HTTP/1.1 messages without requiring application code:

A representative portion of the output looks like:

The > and < prefixes are display markers added by curl; they are not transmitted. Lines beginning with * are also diagnostic messages about DNS, connections, or TLS rather than HTTP fields. The blank > or < line shows the end of the corresponding header section.

Actual fields and values can vary by curl version and by the server. The structure remains the same: request line, request fields, empty line, then response status line, response fields, empty line, and any response body.

The same command with an https:// URL displays the logical HTTP exchanged at the endpoint. A packet capture taken between the endpoints sees encrypted TLS data instead of those plaintext lines.

Common Misunderstandings

The complete URL is not always copied into the request line. A direct HTTP/1.1 request normally sends the path and query as its target, places the authority in Host, and omits the fragment entirely.

The blank line is protocol syntax, not cosmetic formatting. It is the delimiter between the header section and the optional body.

Content-Length is a byte count. It counts encoded body bytes, not characters, lines, JSON properties, headers, or the complete HTTP message.

A body is not necessarily text. HTTP can carry arbitrary binary content, and Content-Type tells the recipient how the bytes are represented.

A status line does not identify the original target. The client associates responses with requests through protocol state, not a URL repeated in every response.

The reason phrase is not a stable API value. Clients make protocol decisions from the numeric status code.

Header names are case-insensitive, but values do not share one universal case rule. Each field defines how its value is interpreted.

One call to recv() does not return one request. The transport can split or combine HTTP bytes at arbitrary positions.

Finding \r\n\r\n does not find the end of every message. It finds the end of the header section; body framing determines the complete message boundary.

Stateless does not mean connectionless. HTTP message semantics do not depend on conversational state, but multiple messages can use an established connection.

HTTPS does not replace HTTP anatomy. It protects the exchange so that intermediaries cannot read or modify the plaintext without participating in TLS.

HTTP/2 and HTTP/3 do not send these textual lines verbatim. They preserve the main semantics while using different binary framing.

Summary

An HTTP exchange contains a client request and server response. In HTTP/1.1, each has a start line, headers, an empty line, and an optional body. A request line carries the method, target, and version; a direct request normally sends the path and query, puts the authority in Host, and omits the URI fragment. A response status line carries the version, three-digit code, and optional reason phrase.

Header names are case-insensitive, but values follow field-specific rules. Control lines end in CRLF, with CRLF CRLF ending the header section. Body length follows framing such as Content-Length, chunked transfer coding, or response connection closure; Content-Length counts body bytes only.

HTTP messages do not align with TCP segments or socket operations. HTTPS protects them with TLS, while newer HTTP versions represent the same concepts with binary frames.

Parse the byte stream by reading the start line, complete headers, and exact body framing without consuming bytes from the next message.

Quiz

HTTP Request/Response Anatomy Quiz

5 quizzes