HTTP/3 is actually a protocol for transporting HTTP messages, not for defining HTTP messages themselves, and it achieves this by running over QUIC, which is the real innovation.
Let’s see it in action. Imagine you’re a browser trying to fetch https://example.com.
First, your browser initiates a connection to example.com’s server. With HTTP/1.1 or HTTP/2, this typically involves a TCP handshake, followed by a TLS handshake. That’s at least two round trips before any HTTP data can even start flowing.
Browser -> Server: SYN
Server -> Browser: SYN-ACK
Browser -> Server: ACK
(TCP Handshake - 1 RTT)
Browser -> Server: Client Hello
Server -> Browser: Server Hello, Certificate, Encrypted Key Exchange
Browser -> Server: Finished
Server -> Browser: Finished
(TLS Handshake - 1-2 RTTs)
Then, HTTP requests and responses fly over this established connection. If there’s packet loss, TCP’s Head-of-Line Blocking (HOLB) rears its ugly head: one lost packet stalls the entire connection, even if subsequent packets have arrived and are ready to be processed.
Now, with HTTP/3, the story changes dramatically. The browser still needs to connect to example.com, but instead of TCP and TLS, it uses QUIC. QUIC itself incorporates TLS 1.3 directly into its handshake.
Browser -> Server: Initial QUIC packet (includes TLS Client Hello)
Server -> Browser: QUIC packets (includes TLS Server Hello, Certificate, etc.)
Browser -> Server: QUIC packet (includes TLS Finished)
Server -> Browser: QUIC packet (includes TLS Finished)
(QUIC/TLS Handshake - typically 1 RTT, sometimes 0 RTT for subsequent connections)
Notice that the QUIC handshake and the TLS handshake are combined. This significantly reduces the connection setup time. But the real magic is what happens after the handshake.
QUIC is built on UDP. This might sound crazy, as UDP is notoriously unreliable. However, QUIC implements its own reliability, congestion control, and security features on top of UDP. Each QUIC connection is actually composed of multiple independent streams.
When your browser requests https://example.com/style.css and https://example.com/script.js, these might be sent over separate QUIC streams within the same QUIC connection.
If a packet for style.css gets lost, only the style.css stream is affected. The script.js stream can continue to deliver its data without waiting for the lost style.css packet. This is QUIC’s stream-level multiplexing, and it effectively eliminates Head-of-Line Blocking at the transport layer.
The key levers you, as a developer, control are often at the HTTP layer, but understanding QUIC’s behavior is crucial for performance.
Alt-SvcHeader: The server tells the browser it supports HTTP/3 by sending anAlt-Svcheader. For example:Alt-Svc: h3=":443"; ma=2592000. This tells the browser that it can reach the same origin (example.com) on UDP port 443 using HTTP/3.0-RTTData: For subsequent connections to a server it has communicated with before, QUIC/HTTP/3 can achieve 0-RTT connection establishment. This means the client can send application data in its very first packet, as it already has the necessary keys from a previous session. This is a massive performance win for repeat visitors.
The most surprising thing about QUIC is that it’s not just a faster TCP. It’s a fundamentally different approach to reliable, secure transport that was designed with the realities of the modern internet (e.g., mobile networks, NAT traversal, encrypted traffic) in mind from the ground up, whereas TCP and TLS evolved over decades and have accumulated complexities.
The QUIC connection ID is a critical concept that allows connections to survive changes in the client’s IP address or port, which is common on mobile devices or when moving between Wi-Fi and cellular networks, without requiring a full re-handshake. This is managed by the connection ID, which is independent of IP addresses and ports.
The next hurdle you’ll likely encounter is understanding how QUIC’s congestion control mechanisms, like BBR or Cubic, interact with application-level retry logic.