The idea: one handshake instead of two
Before a browser can send GET / over HTTPS, two things must be set up: a transport connection that delivers bytes reliably, and TLS keys that encrypt them. With HTTP/2 these are two protocols, TCP and TLS 1.3, and their handshakes run one after the other. QUIC, the transport under HTTP/3, does both in one handshake: the TLS messages ride inside QUIC's first packets. The page loads the same page over both, on the same network with a round-trip time (RTT) of 50 ms, and counts the round trips.
Each side of the canvas is a time-down diagram: the client's line on the left, the server's on the right, and the network between them. Time goes down, one row per one-way trip (25 ms), and the shaded bands are round trips. A packet first appears at its sender, with its contents written next to the sender's line, and then crosses the network. The line under each title says when the request left and when the response arrived.
TCP + TLS 1.3: two handshakes in a row
TCP first runs its three-way handshake: SYN, SYN-ACK, ACK. That costs one round trip and only proves that both sides are there. Then TLS 1.3 runs on top: the client's ClientHello (sent together with the ACK) carries its key share, and the server answers with ServerHello and, already encrypted, EncryptedExtensions, its Certificate, CertificateVerify and Finished. The client checks them and sends its own Finished. Only then may it send the request: 2 RTT after it started, and the response is back after 3 RTT (150 ms) (Demo: new connection, 2 RTT vs 1 RTT). How an HTTPS Connection Is Established shows the TLS messages and keys one by one.
QUIC: TLS inside the transport
QUIC runs over UDP, so there is no SYN. Its first packet is an Initial packet that carries the TLS ClientHello itself, padded to at least 1200 bytes so a fake sender cannot use the server to amplify traffic. The server answers with an Initial packet (ServerHello) and Handshake packets (the encrypted rest of its TLS flight). The client then sends its Finished in a Handshake packet and, in the same flight, the request in a 1-RTT packet. The transport and the keys are ready together, so the request leaves after 1 RTT and the response is back after 2 RTT (100 ms).
| TCP + TLS 1.3 | QUIC | |
|---|---|---|
| new visitor: request leaves at | 2 RTT (100 ms) | 1 RTT (50 ms) |
| new visitor: response at | 3 RTT (150 ms) | 2 RTT (100 ms) |
| returning visitor: request leaves at | 2 RTT (no early data over TCP in browsers) | 0 RTT (0-RTT data) |
| returning visitor: response at | 3 RTT (150 ms) | 1 RTT (50 ms) |
| a lost packet stalls | every stream | only its own stream |
| client changes IP | new connection, 3 RTT | same connection, 1 RTT |
QUIC keeps three separate packet number spaces, one for each kind of key: Initial (keys anyone can derive from the connection ID, so they only protect against accidents), Handshake, and 1-RTT (the real application keys). 0-RTT packets use keys from an earlier session.
0-RTT resumption, and replay
At the end of a visit the server gives the client a session ticket. On the next visit the client offers it as a pre-shared key (PSK) and can encrypt data with it right away: QUIC sends GET / in a 0-RTT packet next to its first Initial packet, and the response is back after 1 RTT (50 ms). TLS 1.3 over TCP also has early data, but browsers don't send it, so a returning visitor still waits for SYN-ACK and a (shorter, certificate-free) TLS round trip (Demo: returning visitor, 0-RTT).
0-RTT data has a weakness: it does not depend on anything fresh from the server, so an attacker who records the first flight can send it again and the server cannot tell the copy from the original. A GET run twice does no harm, but a POST /buy places the order twice (Demo: 0-RTT replay attack).
The defences are in layers. Browsers put only safe, idempotent requests in 0-RTT. Servers keep anti-replay state (single-use tickets, or a record of recent ClientHellos) and may refuse early data. A server or proxy that is not sure can answer 425 Too Early (RFC 8470), and the client sends the request again after the handshake.
Head-of-line blocking
HTTP/2 sends many requests at once as streams, but all of them share one TCP connection, and TCP delivers one ordered byte stream. If one segment is lost, every byte after it waits in the receive buffer until the resend arrives, even bytes of other streams that arrived safely. This is head-of-line blocking. QUIC knows about streams: each frame says which stream and which offset it carries, and each stream is put in order on its own. A lost packet stalls only the streams whose data it carried (Demo: lost packet, head-of-line blocking: on TCP css, js and png all arrive at 95 ms; on QUIC js arrives at 35 ms and png at 45 ms, only css waits). A resend goes in a new packet with a new number, so an acknowledgement is never ambiguous.
Connection migration
A TCP connection is named by its 4-tuple: client IP, client port, server IP, server port. When a phone walks out of Wi-Fi range and gets a new address on the mobile network, no packet matches the old connection any more: the browser must open a new one and pay the handshakes again (3 RTT to the next response). A QUIC packet carries a connection ID chosen by the endpoints (7f3a here), so the server still finds the connection when the address changes. Before it sends much to the new address it checks the path: PATH_CHALLENGE carries a random value and the client echoes it in PATH_RESPONSE. The next response comes back after 1 RTT (Demo: client changes IP, connection migration). The same mechanism survives a NAT that changes the client's port (see How NAT Shares One Public Address).
See also HTTP/1.1 vs HTTP/2 vs HTTP/3 for whole page loads over the three versions, TCP vs UDP for the transport QUIC is built on, and How TCP Congestion Control Works for the loss recovery and congestion window that QUIC also has.
What the page leaves out
Real packet sizes and the anti-amplification limit (a server sends at most three times what it received before the client's address is validated), Retry packets and address validation tokens, version negotiation, QUIC loss detection timers and congestion control (RFC 9002), TCP Fast Open (which also puts data in the SYN), TLS early data over TCP, coalescing several QUIC packets in one UDP datagram, connection ID rotation for privacy, QPACK, Alt-Svc and the DNS HTTPS record that tell a browser to try HTTP/3, and networks that block UDP so browsers fall back to TCP.