Deep Dive: HTTP/3 QUIC Header Compression (8740)
Introduction
HTTP/3 runs over QUIC, a transport protocol that eliminates head‑of‑line blocking at the transport layer. However, header compression must also adapt to QUIC's stream‑oriented nature. QPACK (RFC 9204) is the dedicated header compression scheme for HTTP/3, designed to operate over unordered, potentially lossy streams while preserving the compression efficiency of HPACK.
This article dissects QPACK's architecture, its static and dynamic table management, encoder/decoder stream semantics, and the wire format of header blocks. We also present benchmark data from Votion Cloud's edge fleet and provide a runnable code sandbox for experimenting with QPACK encoding.
QPACK Design Goals
- Stream Independence: Header blocks must be decodable without waiting for other streams.
- Resilience to Reordering: QUIC streams can arrive out of order; QPACK uses explicit acknowledgments for dynamic table updates.
- Compatibility with HPACK: Reuse the same static table and Huffman coding to ease migration.
- Low Latency: Avoid round‑trips for table synchronization; use unidirectional encoder/decoder streams.
Static and Dynamic Tables
QPACK defines a static table of 99 common header fields (identical to HPACK's static table). The dynamic table is managed per connection and has a configurable maximum capacity (default 4 KB). Entries are added via INSERT_WITH_NAME_REFERENCE or INSERT_WITH_LITERAL_NAME instructions on the encoder stream. The decoder acknowledges insertions with INSERT_COUNT_INCREMENT on the decoder stream, ensuring the encoder knows which entries are safe to reference.
Eviction follows a FIFO policy when capacity is exceeded. The encoder must track the known received count to avoid referencing entries not yet acknowledged.
Encoder and Decoder Streams
Two unidirectional QUIC streams carry QPACK control data:
- Encoder Stream (type 0x02): Sent by the encoder (client or server) to convey dynamic table updates and section acknowledgments.
- Decoder Stream (type 0x03): Sent by the decoder to acknowledge received insertions and to signal stream cancellations.
These streams are independent of request/response streams, allowing header compression state to evolve without blocking application data.
Header Block Encoding
Each HTTP/3 request/response carries a Header Block prefixed by a Required Insert Count (varint) and a Base (varint) that defines the reference point for dynamic table indices. The header block consists of a series of Header Field Representations:
- Indexed Header Field: References an entry in the static or dynamic table (prefix 0b1).
- Literal Header Field with Name Reference: Name from table, value literal (prefix 0b01).
- Literal Header Field with Post‑Base Name Reference: Name from dynamic table relative to Base (prefix 0b0000).
- Literal Header Field with Literal Name: Both name and value literals (prefix 0b001).
All integers use QUIC variable‑length integer encoding. String literals may be Huffman‑coded (flag bit 0x80).
Performance Benchmarks
We measured QPACK compression ratio and CPU overhead on Votion Cloud's edge nodes (Intel Xeon Platinum 8380, 2.3 GHz) using a synthetic workload of 10 k requests/sec with header sizes ranging from 200 B to 2 KB.
- Compression Ratio: Median 85% reduction vs. raw headers (HPACK baseline 82%).
- Encoder CPU: 0.12 ms per 1 KB header block (single core).
- Decoder CPU: 0.09 ms per 1 KB header block.
- Dynamic Table Hit Rate: 92% after warm‑up (1000 requests).
The chart below visualizes compression ratio vs. header size for QPACK vs. HPACK over QUIC vs. TCP.
eBPF/XDP kernel filter evaluates TCP/UDP frames directly on server NIC.
Implementation Considerations
- Memory Management: Bound dynamic table memory per connection; use a global pool to avoid fragmentation.
- Stream Prioritization: Encoder/decoder streams should be marked with low QUIC stream priority to avoid starving request streams.
- Error Handling: Malformed header blocks must trigger a
QPACK_DECOMPRESSION_FAILEDconnection error (0x200). - Interoperability: Test against nghttp3, quiche, and msquic implementations; verify static table alignment and Huffman decoding.
Conclusion
QPACK successfully adapts header compression to QUIC's stream model, delivering superior compression ratios with minimal latency impact. By decoupling compression state from request streams via dedicated encoder/decoder streams, it preserves QUIC's head‑of‑line blocking elimination while maintaining HPACK‑level efficiency. Votion Cloud's production deployment shows a 3‑5% improvement in end‑to‑end request latency for header‑heavy workloads (e.g., GraphQL, gRPC‑Web).
For engineers integrating HTTP/3, the key takeaways are: configure dynamic table capacity per tenant, monitor encoder/decoder stream health, and leverage the provided code sandbox to validate custom header sets before rollout.