How HTTP request and response data is encoded, transmitted, and received in binary form
One of the most common misconceptions I encounter is that HTTP is a "text-based" protocol while TCP/IP is "binary." The reality is that everything on the wire is binary. HTTP just happens to use human-readable ASCII characters as its encoding format. When your browser sends GET /index.html HTTP/1.1\r\n, that text is converted byte by byte into its binary representation: 01000111 01000101 01010100 00100000 00101111 01101001 01101110 01100100 01100101 01111000 00101110 01101000 01110100 01101101 01101100 00100000 01001000 01010100 01010100 01010000 00101111 00110001 00101110 00110001 00001101 00001010. Every character in an HTTP message is an ASCII byte, and every byte is an 8-bit binary sequence on the wire. In my network debugging work, I have spent countless hours decoding HTTP traffic at the byte level to find encoding errors, malformed headers, and protocol violations that higher-level tools simply cannot detect.
An HTTP request starts with a request line that contains the method, the path, and the protocol version. Each of these tokens is separated by a space character (00100000 in binary, 0x20 in hex, decimal 32). The line ends with a carriage return (00001101, 0x0D, 13) followed by a line feed (00001010, 0x0A, 10). Let me break down a simple GET request byte by byte:
G (ASCII 71): 01000111E (ASCII 69): 01000101T (ASCII 84): 01010100 (ASCII 32): 00100000/ (ASCII 47): 00101111H (ASCII 72): 01001000T (ASCII 84): 01010100T (ASCII 84): 01010100P (ASCII 80): 01010000/ (ASCII 47): 001011111 (ASCII 49): 00110001. (ASCII 46): 001011101 (ASCII 49): 00110001\r (ASCII 13): 00001101\n (ASCII 10): 00001010I have constructed raw HTTP requests manually — byte by byte — when testing custom HTTP clients and servers. The CRLF sequence (00001101 00001010) is the most common source of bugs: if you use only \n or only \r, strict HTTP parsers will reject your request.
After the request or response line, HTTP headers follow, each on its own line. Each header line has the same binary pattern: the header name in ASCII bytes, a colon (00111010, 0x3A), a space (00100000, 0x20), the header value in ASCII bytes, and a CRLF (00001101 00001010). The header section ends with an empty line — two consecutive CRLFs: 00001101 00001010 00001101 00001010. This double CRLF is the delimiter that separates headers from the body. In my experience writing HTTP parsers, detecting this 4-byte sequence is the most critical parsing step. Any HTTP parser worth its salt scans the byte stream for two consecutive CRLF pairs to find the boundary between the header section and the body.
For example, the header Host: example.com in binary is: 01001000 01101111 01110011 01110100 00111010 00100000 01100101 01111000 01100001 01101101 01110000 01101100 01100101 00101110 01100011 01101111 01101101 00001101 00001010. That is 19 bytes: "Host: example.com" followed by CRLF.
The Content-Length header tells the receiver exactly how many bytes of body data follow the header section. This is a critical binary boundary: after parsing all headers and finding the double CRLF, the receiver reads exactly Content-Length bytes from the TCP stream. The Content-Length value is transmitted as ASCII digits, but each digit is a binary byte. For example, Content-Length: 531\r\n has the digits "5", "3", "1" as ASCII bytes — 00110101 00110011 00110001. The receiver must parse these ASCII digits into the integer 531, then read exactly 531 bytes from the stream. I have debugged many HTTP transfer issues caused by mismatched Content-Length values — if the real body is 532 bytes and the header says 531, the extra byte leaks into the next response.
When the server does not know the body size in advance (dynamic content), it uses chunked transfer encoding. The body is split into chunks, each prefixed with a hexadecimal size in ASCII, followed by CRLF, the chunk data, and another CRLF. For example, a chunk of 42 bytes starts with the ASCII string "2A" (00110010 01000001), then CRLF (00001101 00001010), then 42 bytes of data, then CRLF. The transfer ends with a zero-length chunk: "0" followed by CRLF, a final CRLF. I have traced through chunked encodings byte by byte when debugging streaming media issues, and the binary structure is always the same: ASCII hex size, CRLF, data, CRLF, repeat until a zero-size chunk.
HTTP/2 abandoned the text-based approach of HTTP/1.1 entirely and adopted a true binary framing layer. Each HTTP/2 frame has a 9-byte header: Length (24 bits), Type (8 bits), Flags (8 bits), Stream Identifier (31 bits, with 1 reserved bit), followed by the frame payload. The Type field (00000000 = DATA, 00000001 = HEADERS, 00000100 = SETTINGS, etc.) tells the receiver how to interpret the payload. The Stream Identifier is a 31-bit unsigned integer that allows multiplexing — multiple concurrent streams over a single TCP connection. I've analyzed HTTP/2 frames at the binary level to understand how multiplexing works, and the 31-bit stream ID is a clever design choice: 31 bits gives 2 billion possible concurrent streams, but the reserved high bit allows compatibility with systems that mistakenly interpret it as a signed 32-bit integer.
Pro Tip: The double CRLF (0D 0A 0D 0A in hex) is the single most important byte sequence in HTTP parsing. If you are writing a network tool and your HTTP parsing is broken, the first thing to check is that you are correctly detecting two consecutive CRLF sequences as the header-body delimiter. In ASCII, this binary sequence represents no visible characters, so it can be tricky to spot in raw dumps.
HTTPS wraps the entire HTTP conversation inside TLS encryption. The HTTP message — including the request line, headers, and body — is encrypted before being sent over TCP. The only visible binary data is the TLS handshake: the ClientHello, ServerHello, certificate exchange, and key negotiation. After the handshake, everything becomes an encrypted binary stream that looks like random bits to any observer. In my security work, I've analyzed TLS handshakes at the binary level to verify cipher suite negotiation and certificate chain validity. The TLS record layer uses a 5-byte header: Content Type (8 bits), Protocol Version (16 bits), and Length (16 bits). The content type 00010111 (23) indicates application data — the encrypted HTTP content.
HTTP status codes in the response line come as three ASCII digits. The first digit defines the class of the response. In binary, the most common codes are:
200 OK: 00110010 00110000 00110000 — The "everything is fine" response.301 Moved Permanently: 00110011 00110000 00110001 — Permanent redirect.404 Not Found: 00110100 00110000 00110100 — Resource does not exist.500 Internal Server Error: 00110101 00110000 00110000 — Server-side failure.Each digit is an ASCII character, not a raw binary number. The first digit class (1xx=1, 2xx=2, etc.) determines whether the receiver continues, succeeds, redirects, errors on the client side, or errors on the server side.
Open the Binary Code Decoder in a new tab and enter some binary patterns to see the results instantly. All conversions happen in your browser — no data is sent to any server.