The HTTP exchange, by hand
◈ 5 cardsHTTP as four steps over TCP — open port 80, send a request, read the response, close — driven by hand with telnet, and what HTTP/1.1 changes about step 4.
HTTP is a text protocol you can type
HTTP — the Hypertext Transfer Protocol — is the set of rules a web client and a web server follow when one asks for a resource and the other answers. Two things about it surprise people. First, it is plain text: every request and every response begins with lines of ASCII you can read with your eyes and produce with your fingers. Second, it is data-format agnostic. Nothing in the protocol knows or cares whether the bytes it moves are HTML, a PNG, a JAR file, or a firmware image; a header called Content-Type declares what they are, and HTTP just carries them.
HTTP runs on top of TCP, and that layering is not decoration. TCP gives you a reliable, ordered byte stream between two endpoints; HTTP spends that stream on one request and one reply. Every failure you debug in this module is therefore one of two kinds — a TCP failure, where nothing connects, or an HTTP failure, where something connects and then sits there saying nothing. Telling those apart is the first skill this module teaches.
The four steps
For each request, the exchange is four steps:
- The client opens a TCP connection to the server — port 80 by default, or whatever port the URL names.
- The client sends a request: a request line, header lines, a blank line, and optionally a body.
- The server sends a response: a status line, header lines, a blank line, and the requested resource or an error page.
- The connection closes.
That is HTTP/1.0 exactly. HTTP/1.1 changed one thing about it: the server no longer has to close after responding, so steps 2 and 3 may repeat many times over a single connection before step 4 finally happens. The lockstep is unchanged — one request, then one response, then the next request. Only the socket is reused.
Telnet is the protocol debugger
telnet host port opens a raw TCP connection and hands you the keyboard: whatever you type goes out as bytes, whatever comes back is printed. That makes it the most useful tool you own while writing network code, for one reason — it isolates one side of the pair. When your client cannot talk to your server you have two suspects and no evidence. Telnet the server by hand: if it answers correctly, the server is exonerated and the bug is in your client. Reverse the trick to test a client against a known-good server.
Worked example — a hand-typed GET and its answer
Open the connection, then type three lines and one empty one:
$ telnet example.org 80
Trying 93.184.216.34...
Connected to example.org.
Escape character is '^]'.
GET /index.html HTTP/1.1
Host: example.org
Connection: close
The first four lines are telnet talking to you; the last four are you talking to HTTP. Nothing happens until that final empty line arrives — it is the signal that the header block is over. The instant it lands, the server answers:
HTTP/1.1 200 OK
Date: Mon, 03 Aug 2026 12:04:11 GMT
Server: Apache
Content-Type: text/html; charset=UTF-8
Content-Length: 98
Connection: close
<html><head><title>Example</title></head>
<body><p>Hello from a hand-typed GET.</p></body></html>
Read that in three parts. The status line HTTP/1.1 200 OK is the verdict. The headers are metadata, one Keyword: Value per line: Content-Length: 98 is the exact count of body bytes that follow, and Connection: close is the server agreeing to perform step 4. Then a blank line, then exactly 98 bytes of body. Because you asked for close, telnet prints Connection closed by foreign host. and drops you back at the shell — which is itself evidence, because the server did precisely what its header promised.