//everybyteexplained HTTP/2 connection preface and settings handshake

Adhityaa Chandrasekar
May 30, 2025

Subtrace is a zero‑code, zero‑root network tracer that just works. We volunteer as tribute to read raw HTTP/2 hex so that you don't have to, but if you’re the zoom-all-the-way-in type, look no further.

The preface

"PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"

HTTP/2 connections always start with this fixed 24-byte magic ritual called a preface.

The goal is to send bytes that look kinda like HTTP/1.1 but are certain to be invalid so that we can deal with proxies and middleboxes stuck in a HTTP/1.1 worldview.

The idea is that these old servers would parse it as a PRI request, which isn't a valid HTTP method, so they will drop the connection rather than mangle it.

Fun fact: Since there are infinitely many ways to construct invalid HTTP/1.1-looking requests, the preface went through a bikeshedding decathlon in earlier drafts. The original proposal was FOO..BAR but then it quickly became FOO..BA to shave a byte. Two weeks later, it was changed to STA..RT but that too lasted a mere 27 hours before we got PRI..SM finally.

Everything else in HTTP/2 is binary but the preface is pure plaintext. This way, endpoints can prove to each other they speak a specific binary protocol without leaking any binary so as to not upset any proxies in the middle.

The SETTINGS frame

After the preface, everything in HTTP/2 happens in frames.

The client sends a SETTINGS frame to set the parameters of the connection. In this example, everything after offset 24 is that frame.

Let's break it down.

The header

The first eight bytes of every frame is the header.

00 00 12

3 bytes to indicate the frame size. This is just the payload size and does not include the size of the header itself.

04

1 byte to indicate the frame type. There are twelve different frame types.

00

1 byte for frame-specific flags. When the server acknowledges the client's settings, it will flip the ACK bit.

00 00 00

3 bytes to indicate the stream ID. HTTP/2 can multiplex many streams in a single connection and streams are identified by this ID. Zero means this is a connection-level frame.

The payload

The rest of the frame is the payload.

The meaning of its content depends on the frame type. It can be empty sometimes, but it's 18 bytes here.

SETTINGS frame payloads are always in six byte multiples. Each six byte chunk is a key-value parameter.

Let's look at each parameter.

1

00 03

Indicates this is the MAX_CONCURRENT_STREAMS parameter.

00 00 ff ff

The client wants the maximum number of concurrent streams in the connection set to 65,535, which is practically infinite.

2

00 02

Indicate this is the ENABLE_PUSH parameter.

00 00 00 00

The client wants to disable server push but these most CDNs have disabled server push on their end anyway because it wasn't worth it at scale.

3

00 04

Indicate this is the INITIAL_WINDOW_SIZE parameter.

00 10 00 00

The client wants 16x the default window size, which is great for long, fat data pipes but can also cause flow control stalls sometimes.

Next up

Next week, we'll vivisect HPACK compression and watch header entropy collapse. Bring popcorn.