Skip to main content

HTTP

HTTP is the request/response protocol of the web. The client sends a request naming a method and a resource; the server sends back a response with a status code and, usually, a body. It runs over a reliable transport (TCP), so HTTP itself only has to define the message format and the exchange.

Pick a method and a response status, then step through the exchange:

request line / headersuccess (2xx)redirection (3xx)client error (4xx)server error (5xx)
Client
Server
Step through one HTTP request and response.
step 0 / 9

The request

A request starts with a request line - method, target, and version - then a block of headers, then an optional body.

  • GET - retrieve a resource. No body; safe and idempotent.
  • POST - submit data to be processed (create a resource, run an action).
  • PUT - replace a resource with the supplied body; idempotent.
  • DELETE - remove a resource; idempotent.

Headers carry metadata: Host names the site (required in HTTP/1.1), Accept states what formats the client wants, Content-Type describes the body.

Worked example: a request and response on the wire

A real GET request for a small HTML page looks like this, byte for byte (\r\n ends each line, and a blank line ends the headers):

GET /index.html HTTP/1.1\r\n
Host: www.example.com\r\n
Accept: text/html\r\n
\r\n

That is 3 header lines plus the blank terminator - about 60 bytes total, and no body at all for a GET. The response might be:

HTTP/1.1 200 OK\r\n
Content-Type: text/html\r\n
Content-Length: 137\r\n
\r\n
<html>...137 bytes of markup...</html>

The status line and headers here add roughly 45 bytes of overhead on top of the 137-byte body - for a request this small the headers are a meaningful fraction of the traffic, which is one reason HTTP/2's header compression (HPACK) exists: it matters much more once a page needs 50+ small requests for scripts, styles, and images.

The response and status codes

The response opens with a status line - version, a three-digit code, and a reason phrase - followed by headers and the body. The first digit puts the code in a class:

ClassMeaningExample
1xxInformational100 Continue
2xxSuccess200 OK, 201 Created
3xxRedirection301 Moved Permanently, 304 Not Modified
4xxClient error404 Not Found
5xxServer error500 Internal Server Error

The class tells you who is at fault or what to do next: a 4xx means the request was wrong, a 5xx means the server failed a valid request, a 3xx tells the client to look elsewhere.

Non-persistent vs persistent connections

Early HTTP opened a fresh TCP connection for every object and closed it after one response - non-persistent. Each object then paid a full TCP handshake of latency, and a page with many images was painfully slow.

HTTP/1.1 defaults to persistent connections (Connection: keep-alive): the TCP connection stays open and is reused for many request/response pairs. One handshake, many objects - far less latency and overhead.

caution

Persistent connections still send requests in order on the connection. A slow response blocks the ones queued behind it - head-of-line blocking - which is exactly what the newer HTTP versions set out to fix.

Request smuggling from a Content-Length / Transfer-Encoding mismatch

On a persistent connection, the server has to know exactly where one request ends and the next begins, purely from headers - either a Content-Length byte count or Transfer-Encoding: chunked. If a request smuggles both headers, or a front-end proxy and the backend server disagree on which one to trust, they can disagree about where the request body ends. One server reads extra bytes as the start of the next request on the same connection - HTTP request smuggling - letting an attacker's crafted request get processed as if it came from the victim whose request follows it on the shared connection. This is a real, still-current class of production vulnerability behind reverse proxies and CDNs, and it exists specifically because persistent connections require both ends to agree on framing with no independent way to verify it.