In a WebSocket frame, how is payload length encoded when the 7-bit length field holds 126 or 127?
answer
- seven bits, two escape values
- 126 and 127 are switches
- two more bytes, or eight
- network byte order, minimal encoding
- 2, 4 or 10 header bytes unmasked
basics
~20 sValues 0-125 are the length itself. 126 means the next 2 bytes hold a 16-bit unsigned length; 127 means the next 8 bytes hold a 64-bit unsigned length. Both are network byte order, so a small frame costs 2 header bytes and a huge one costs 10.
solid answer
~40 sThe second header byte holds the `MASK` bit plus a 7-bit length field, and that field is a three-way switch. If it reads 0-125, that *is* the payload length and nothing follows. If it reads `126`, the next **2 bytes** are the real length as a 16-bit unsigned integer. If it reads `127`, the next **8 bytes** are the real length as a 64-bit unsigned integer whose most significant bit must be 0. Multi-byte lengths are in network byte order. The effect is a header that scales with the payload: two bytes for a short command, four for a few hundred bytes, ten for anything large — plus four more when the frame is masked, since the `Masking-key` follows the length fields.
code
http · 8 lines# 100-byte binary frame, unmasked: 2 header bytes
82 64 <100 bytes>
# 256-byte binary frame, unmasked: 126 escape + 2 length bytes
82 7e 01 00 <256 bytes>
# 100 KB binary frame, unmasked: 127 escape + 8 length bytes
82 7f 00 00 00 00 00 01 86 a0 <102400 bytes>go deeper
Remember the three cases: 0-125 is the length itself, 126 means two more length bytes follow, 127 means eight more follow, all most significant byte first.
Explain the trade-off the variable encoding buys — two header bytes for a tiny frame instead of a fixed eight — and compute the header size for a masked frame, which adds the 4-byte key.
Treat the declared length as untrusted: enforce a configured maximum frame and message size so an eight-byte field cannot make your process allocate gigabytes.
The framing overhead is bounded and tiny, so when you argue a transport choice, argue message rate, payload design and connection cost — not header bytes.
## Three encodings behind one field After the first byte's FIN, RSV and opcode bits comes a second byte: one `MASK` bit and a **7-bit payload length**. Seven bits reach 127, but a frame may carry far more than 127 bytes, so two of the values are spent as escapes: | 7-bit field | what follows | payload length is | |---|---|---| | 0-125 | nothing | the field's own value | | `126` | 2 bytes | those bytes as a 16-bit unsigned integer | | `127` | 8 bytes | those bytes as a 64-bit unsigned integer, most significant bit 0 | The extended length is in **network byte order** (most significant byte first). The specification also requires the **minimal** encoding: a 200-byte payload uses the `126` form, and it would be a protocol violation to pad it into the `127` form even though eight bytes can obviously hold 200. ## What the header actually costs Add it up, because the arithmetic is the point of the design: 1. **2 bytes** — the two fixed header bytes, always. 2. **+0, +2 or +8 bytes** — the extended length, if any. 3. **+0 or +4 bytes** — the `Masking-key`, present exactly when the `MASK` bit is set. So the envelope around a payload ranges from **2 bytes** (a short, unmasked, server-to-client frame) to **14 bytes** (a huge, masked, client-to-server frame). An unmasked frame with a large payload sits at 10. That range is why a WebSocket connection is so often contrasted with per-message HTTP requests: a one-byte status update travels under a 2-byte or 6-byte envelope rather than under a request line and a block of header fields. The saving is real and it is largest exactly where updates are small and frequent. ## Why not simply always use eight bytes A fixed 8-byte length field would be simpler to parse and would cost eight bytes on every frame including the tiny ones. On a connection whose traffic is dominated by short control messages — a few bytes each, many per second, across many connections — that overhead would dwarf the payload. The variable encoding makes the common case cheap and lets the rare case be enormous, which is the same trade-off a variable-length integer makes anywhere else. ## Reading a length safely This field is a receiver's first real decision point, and a few rules protect it: - **Read the escape before the bytes.** A parser that reads 0-125 and then skips two bytes anyway will desynchronise the stream permanently, because frames are back to back with no delimiter to resynchronise on. - **Reject a 64-bit length with the top bit set.** The most significant bit of the 8-byte form must be 0, so the largest legal value is 2^63 - 1. - **Do not allocate on the strength of the declared length.** A frame can *declare* a length far larger than the receiver is willing to hold. An endpoint that sizes a buffer from an untrusted length field hands an attacker a memory exhaustion primitive with eight bytes of input; the standard answer is a configured maximum, and a frame above it is refused rather than buffered. - **Length is per frame, not per message.** When a message is split, each frame declares its own length and the message's total is never stated anywhere on the wire. A receiver that needs a total has to sum as it goes, which is another reason to enforce a ceiling. ## Worked example ```http 82 7e 01 00 <256 payload bytes> ``` `0x82` is FIN set, opcode `%x2` (binary). `0x7e` is 126 with the `MASK` bit clear, so the next two bytes are the length: `0x0100` = 256. Total header, 4 bytes. Had the payload been 100 bytes, the second byte would have read `0x64` and the header would have been 2 bytes. Had it been 100 KB, the second byte would have read `0x7f` and eight length bytes would have followed, for a 10-byte header. The practical reading for an engineer: header overhead per frame is bounded and small, so the cost that matters on a busy connection is not the framing — it is how many frames you send and what you put in them.
- May a sender use the 8-byte length form for a 200-byte payload?No. The specification requires the minimal encoding for the length, so 200 bytes must use the `126` escape with a 2-byte length. A receiver is entitled to treat a non-minimal length as a protocol error, and accepting one quietly would leave two encodings of the same frame on the wire.
- A frame declares a 4 GB payload. What should the receiver do?Refuse it rather than allocate for it. The declared length is untrusted input, and sizing a buffer from it turns eight header bytes into a memory exhaustion attack. Endpoints configure a maximum acceptable frame or message size and fail the connection when a frame declares more.
saying these in an interview costs you the question
- Thinks a field value of 126 means a 126-byte payload
- Believes every frame carries an 8-byte length
- Reads multi-byte lengths in little-endian order
- Allocates a buffer from the declared length before checking it
- Thinks the length covers the whole message, not one frame