Mastering HTTP/3 QUIC Header Compression (6389)
Technical Overview
HTTP/3 leverages QUIC as its transport layer, and header compression is handled by QPACK (RFC 9204), a variation of HPACK designed for QUIC's stream multiplexing. This article dissects the QPACK dynamic table management, encoder/decoder synchronization, and the impact of stream cancellation on header compression efficiency. We'll explore how the 6389-byte maximum dynamic table size influences memory pressure on high-concurrency servers and how to tune the SETTINGS_QPACK_MAX_TABLE_CAPACITY and SETTINGS_QPACK_BLOCKED_STREAMS parameters for optimal throughput.
QPACK Dynamic Table Mechanics
The QPACK dynamic table stores header field entries referenced by index. Unlike HPACK, QPACK uses a unidirectional encoder stream and decoder stream to convey table updates, decoupling header compression from request/response streams. This design eliminates head-of-line blocking but introduces complexity in managing insertion order and eviction policies. We'll examine the Insert With Name Reference and Insert Without Name Reference instructions, and how the Duplicate instruction reduces redundancy for cookie-like headers.
Benchmarking Header Compression Overhead
We ran a series of microbenchmarks on a 32-core AMD EPYC 7763 node with 256 GiB RAM, using a custom QUIC endpoint built on quiche. The test matrix varied dynamic table capacity (1 KiB, 4 KiB, 16 KiB, 64 KiB) and concurrent streams (10, 100, 1000). Results show that a 4 KiB table yields the best compression ratio for typical REST workloads, while 16 KiB benefits GraphQL-heavy traffic with large header sets. The chart below visualizes bytes saved per request versus table size.
Stream Cancellation and Table Synchronization
When a QUIC stream is reset (e.g., via STOP_SENDING), the decoder may have already processed header blocks that reference dynamic table entries inserted after the cancelled stream's encoder instructions. QPACK handles this via the Section Acknowledgment mechanism: the decoder acknowledges the highest instruction index it has processed, allowing the encoder to safely evict unreferenced entries. Misconfiguration of SETTINGS_QPACK_BLOCKED_STREAMS can cause decoder blocking, leading to increased latency. We provide a tuning checklist for high-churn environments.
eBPF/XDP kernel filter evaluates TCP/UDP frames directly on server NIC.
Production Tuning Checklist
- Set
SETTINGS_QPACK_MAX_TABLE_CAPACITYto 4096 for general-purpose APIs; increase to 16384 for GraphQL gateways. - Configure
SETTINGS_QPACK_BLOCKED_STREAMSto at least 100 to avoid decoder stalls under bursty traffic. - Monitor
qpack_dynamic_table_sizeandqpack_blocked_streamsmetrics via Prometheus; alert on sustained blocked streams > 10. - Enable
QPACK_INSERT_COUNT_INCREMENTtelemetry to detect encoder/decoder desynchronization. - Use connection-level flow control to bound memory consumption of dynamic tables across thousands of connections.