---
title: "QUIC Protocol Explained: How It Works on the Wire"
description: "How the QUIC protocol works: connection setup, streams, loss recovery, encryption and connection migration, and why HTTP/3 runs on it."
---

[Content Delivery Network Blog](https://blog.blazingcdn.com/en-us)

# [QUIC Protocol Explained: How It Works on the Wire](https://blog.blazingcdn.com/en-us/quic-protocol-internals-how-it-actually-works-on-the-wire)

 Written by [Vadym Borovskyi](https://blog.blazingcdn.com/en-us/author/blazingcdn) | Oct 27, 2026, 8:03:22 AM

The QUIC protocol is a UDP-based transport, published as RFC 9000 in 2021, that merges the TLS 1.3 handshake into its own handshake. A fresh connection can send its first request after one round trip instead of the two that TCP plus TLS 1.3 needs, which saves 100 ms on a 100 ms path before any server work begins. This article covers what QUIC does in plain terms and then how it behaves on the wire: the handshake, packet headers, frames, loss recovery and migration. It also includes a QUIC vs TCP table and a failure-mode matrix for debugging HTTP/3 in production.

As of 2026, QUIC (RFC 9000, RFC 9001 and RFC 9002) is the transport under HTTP/3 (RFC 9114). A new QUIC connection is ready for application data after 1 round trip, a resumed connection can carry 0-RTT data in its first flight, and packet loss on one stream does not stall the other streams.

## **What the QUIC protocol does, in plain terms**

TCP gives you one ordered byte stream between two address and port pairs, and TLS encrypts that stream from above. QUIC replaces both layers with a single protocol. It carries many independent streams, it makes encryption mandatory, and it identifies a connection by a connection ID rather than by the 4-tuple.

UDP is only the envelope. It gets packets through NATs and firewalls that would drop a new IP protocol. Everything TCP used to do (reliability, ordering, flow control, congestion control) is rebuilt inside QUIC and encrypted. Most implementations run in user space, so the transport ships with the application and changes on a release cycle rather than a kernel cycle.

The cost is visibility. Middleboxes and packet captures see far less than they do with TCP, and kernel offloads built for TCP over two decades do not apply directly. The [**web performance articles on HTTP/3 and transport latency**](https://blog.blazingcdn.com/web-performance?hsLang=en-us) cover the HTTP layer. This piece stays on the transport.

## **How the QUIC handshake works**

The QUIC handshake carries TLS 1.3 messages inside CRYPTO frames. The client sends an Initial packet padded to at least 1,200 bytes that contains the ClientHello. The server answers with Initial and Handshake packets carrying ServerHello, the certificate chain and Finished. After one round trip, both sides hold 1-RTT keys and application data flows.

The handshake moves through encryption levels, but there are only three packet number spaces: Initial, Handshake and application data. The application data space is shared by 0-RTT and 1-RTT packets. An ACK frame acknowledges packets only in its own space, and each space keeps its own loss detection state. Initial keys are derived from the client's destination connection ID and a version-specific salt. Anyone can decrypt Initial packets, so they provide integrity against off-path tampering, not confidentiality.

### Anti-amplification limits and Retry

A server that has not yet validated the client address may send no more than three times the bytes it has received from that address. This limit prevents QUIC from becoming a reflection amplifier for spoofed UDP. A server under load can instead answer with a Retry packet that carries a token. The client must echo that token, which proves it owns the address but costs one full extra round trip.

### Worked example: when a 1-RTT handshake becomes 2 RTT

Take a client Initial of exactly 1,200 bytes. The server's budget before validation is 3 x 1,200 = 3,600 bytes. Its first flight includes ServerHello, EncryptedExtensions, Certificate, CertificateVerify and Finished, plus packet and AEAD overhead. Our estimate for a typical RSA-2048 chain with one intermediate is 3 to 5 KB on the wire. A compact ECDSA chain usually lands closer to 1.5 to 2.5 KB.

If the flight exceeds 3,600 bytes, the server sends what it can and then stops. It waits for the client's next padded datagram (usually an ACK) to earn another 3,600 bytes. On a 100 ms path, that pause adds about 100 ms. The handshake advantage over TCP plus TLS 1.3 then disappears entirely. Shrinking the chain, switching to ECDSA certificates or enabling TLS certificate compression (RFC 8879) keeps the first flight inside the budget.

### 0-RTT and replay

On a resumed connection, the client can send application data in 0-RTT packets alongside its Initial. An attacker can replay that data, and QUIC does not prevent it. Restrict 0-RTT to idempotent requests, or have the server reject early data for anything with side effects.

## **QUIC packet format on the wire**

QUIC uses two header types, and the first bit of the first byte tells them apart.

- **Long header:** used by Initial, 0-RTT, Handshake and Retry packets. It carries the version, destination and source connection IDs with explicit lengths, and the packet type. Initial, 0-RTT and Handshake packets also carry a length field and a 1 to 4 byte truncated packet number. Retry packets have no packet number. Version Negotiation uses a long-header layout defined as version-independent in RFC 8999.
- **Short header:** used by 1-RTT packets. After the flags byte (spin bit, key phase, packet number length) come the destination connection ID and the packet number. There is no length field, so a short-header packet must be the last one in its UDP datagram.

Header protection masks the low bits of the first byte and the packet number, using a sample of the packet's own ciphertext. A passive capture therefore cannot read packet numbers, key phase or packet number length. A server's first flight usually coalesces an Initial and a Handshake packet into one datagram. Decoding any of this requires qlog output or an exported TLS key log in addition to the raw capture.

## **Frames, streams and loss recovery**

Each packet carries one or more encrypted frames, and the frames carry the transport semantics:

- **STREAM:** application data with stream ID, offset and FIN.
- **ACK:** received packet number ranges plus the ACK delay.
- **CRYPTO:** TLS handshake bytes.
- **MAX\_DATA, MAX\_STREAM\_DATA, MAX\_STREAMS:** connection-level, stream-level and stream-count flow control.
- **PATH\_CHALLENGE and PATH\_RESPONSE:** new-path validation with 8 random bytes.
- **NEW\_CONNECTION\_ID and RETIRE\_CONNECTION\_ID:** connection ID rotation.
- **CONNECTION\_CLOSE:** transport or application termination.

Stream IDs are 62-bit integers. Their two low bits encode the initiator and the direction: client bidirectional streams are 0, 4, 8 and so on, and server bidirectional streams are 1, 5, 9. A lost STREAM frame delays only its own stream, which removes the transport head-of-line blocking that HTTP/2 suffers over TCP.

Packet numbers are never reused. Lost frames are re-sent in new packets with new numbers, while STREAM offsets handle reassembly. This removes TCP's retransmission ambiguity, so every ACK yields a clean RTT sample. Under RFC 9002, a packet counts as lost when a packet sent 3 numbers later has been acknowledged, or after 9/8 of the RTT. A probe timeout (PTO) replaces TCP's retransmission timeout. With no RTT sample yet, the initial RTT is 333 ms and the first PTO is roughly 1 second. That is why a lost client Initial shows up as a 1-second handshake outlier.

## **Connection migration and connection IDs**

Connection IDs decouple a connection's identity from the 4-tuple. A phone moving from Wi-Fi to cellular, or a client behind a NAT that rebinds its port, keeps the same connection. Only the client migrates actively, and a server can refuse with the disable\_active\_migration transport parameter. The new path must pass PATH\_CHALLENGE validation, and anti-amplification limits apply on it until validation succeeds.

For privacy, the client switches to a fresh connection ID from the server's NEW\_CONNECTION\_ID pool, so observers cannot link the old path to the new one. That creates a load balancer problem, because a 4-tuple hash no longer finds the right server. The IETF QUIC-LB draft addresses this by encoding a server identifier into the connection IDs the server issues. If a packet reaches a server without state, that server can answer with a stateless reset that carries a 16-byte token.

## **QUIC vs TCP: which differences matter in production**

QUIC differs from TCP plus TLS 1.3 in five places that change operations: handshake latency (1 RTT vs 2), per-stream loss isolation, encrypted transport headers, connection identity based on IDs, and a user-space implementation. The first two help users on lossy, high-RTT paths. The last three raise CPU cost and make debugging harder.

| Dimension | TCP plus TLS 1.3 | QUIC |
| --- | --- | --- |
| New connection to first request | 2 RTT | 1 RTT (2 if Retry or amplification-limited) |
| Resumed connection | 1 RTT for TCP, then TLS early data | 0-RTT data in the first flight |
| Head-of-line blocking | One loss stalls every multiplexed stream | Loss stalls only the affected stream |
| Retransmission | Same sequence numbers, ambiguous RTT samples | New packet numbers, unambiguous samples |
| Header visibility | Sequence numbers, ACKs and flags in plaintext | Packet numbers and ACKs encrypted; only connection IDs and spin bit visible |
| Connection identity | 4-tuple; a network change breaks the connection | Connection ID; survives NAT rebinding and network changes |
| Implementation | Kernel, with mature segmentation and TLS offloads | Mostly user space; relies on UDP GSO and GRO for efficiency |

QUIC wins on latency and loss isolation, while TCP plus TLS still wins on CPU efficiency per byte and on how easy it is to observe.

## **Failure modes: what breaks, how it shows up, how to confirm it**

| Failure | Symptom | How to confirm |
| --- | --- | --- |
| UDP 443 blocked or rate-limited | Clients silently fall back to TCP; HTTP/3 share drops on specific networks | Protocol share per client ASN; Initials received with no Handshake completion |
| Amplification stall | Handshake takes about 2 RTT instead of 1 | qlog shows the server blocked by the amplification limit; compare certificate chain size to 3x Initial size |
| Lost Initial | Handshake outliers near 1 s at p99 | PTO expirations counted in the Initial space |
| NAT binding timeout | Idle connections die; the next request pays a full handshake | Idle timeout vs observed NAT UDP timeouts (often 30 s or less); stateless reset rate |
| Connection ID misrouting | Migrated or rebound clients reset and reconnect | Stateless resets that correlate with path changes; load balancer hashing on the 4-tuple |
| Path MTU black hole | Stalls on large transfers; small requests succeed | Failed PMTU probes; repeated loss of full-size datagrams only |

Most QUIC incidents present as latency regressions or silent fallback to TCP rather than hard errors, so they only appear if you measure them per packet number space and per network.

## **Verify QUIC behavior on your own stack**

Track handshake duration split by Initial and Handshake space, with PTO counts in each. Record Retry rate and token validation failures, 0-RTT acceptance rate, Initial datagram sizes, migration success, path validation latency and stateless resets. Capture full UDP payloads, not truncated snap lengths, and enable qlog on a sampled share of connections so encrypted traces can be decoded later.

## **Where QUIC fits and where TCP still wins**

QUIC pays off for mobile clients that change networks, for short request-heavy sessions where handshake RTT dominates, for high handshake volume, and for lossy paths where per-stream isolation matters. It is a weaker fit for bulk transfer from a few well-connected servers, where TCP with kernel and NIC offloads moves more bytes per CPU core. It is also a weaker fit for networks that filter UDP, and for teams whose debugging depends on plaintext TCP headers.

## **FAQ: QUIC protocol and the QUIC handshake**

### What is the QUIC protocol, explained simply?

The QUIC protocol is an encrypted transport that runs over UDP and replaces the TCP plus TLS pair. It provides reliable, independent streams, completes a new connection in one round trip, and keeps a connection alive across network changes using connection IDs. It is defined in RFC 9000 and is the transport underneath HTTP/3.

### Why does QUIC use UDP instead of a new transport protocol?

QUIC uses UDP because NATs and firewalls across the internet already pass UDP but drop unknown IP protocols. UDP adds only ports and a checksum, so QUIC rebuilds reliability, ordering, flow control and congestion control on top. Running in user space also lets implementations ship protocol changes with the application instead of waiting for kernel upgrades.

### How many round trips does the QUIC handshake take?

A new QUIC handshake takes one round trip before the client can send application data, compared with two for TCP plus TLS 1.3. Resumed connections can send 0-RTT data in the first flight. A Retry, or a certificate chain larger than the three-times amplification budget, adds a round trip and erases the gap.

### Is QUIC faster than TCP in every case?

QUIC is not faster than TCP in every case. It cuts handshake latency and isolates loss per stream, which helps on mobile and lossy paths. For bulk transfers on clean, low-RTT links, TCP often matches it on latency and uses less CPU per byte thanks to mature kernel and NIC offloads.

### Can firewalls and network monitoring tools see inside QUIC traffic?

Firewalls and passive monitoring tools see very little inside QUIC traffic. Payloads, packet numbers and ACK information are encrypted or header-protected. Only the version on long headers, the connection IDs and an optional spin bit for RTT estimation remain visible. Detailed analysis needs endpoint qlog output or exported TLS keys.

## **Measure your QUIC handshake before you tune it**

This week, pull a sample of qlog traces from your HTTP/3 endpoint and check three numbers: the share of handshakes that took 2 RTT instead of 1, your certificate chain size compared with 3,600 bytes, and PTO counts in the Initial space. If the first number exceeds a few percent, switch to an ECDSA chain or enable certificate compression, then re-measure. Then compare the HTTP/3 share per client network to find where UDP is being filtered.

Heavy static delivery (video segments, installers, game patches) usually runs fine on TCP. BlazingCDN serves that traffic over HTTP/2 and TLS 1.3 rather than HTTP/3. Its [**BlazingCDN delivery features and protocol support**](https://blazingcdn.com/features/) page lists what is included on every plan, along with the 14-day testing period on real production traffic.

[View full post](https://blog.blazingcdn.com/en-us/quic-protocol-internals-how-it-actually-works-on-the-wire)

```json
{
  "@context" : "http://schema.org",
  "@type" : "BlogPosting",
  "author" : {
    "@type" : "Person",
    "name" : "Vadym Borovskyi"
  },
  "dateModified" : "2026-10-27T08:03:22.915Z",
  "datePublished" : "2026-10-27T08:03:22Z",
  "headline" : "QUIC Protocol Explained: How It Works on the Wire",
  "image" : {
    "@type" : "ImageObject",
    "height" : 768,
    "url" : "https://143144902.fs1.hubspotusercontent-eu1.net/hubfs/143144902/Gemini-Blog/image-Oct-27-2026-08-00-43-1112-AM.jpeg",
    "width" : 1376
  },
  "mainEntityOfPage" : "https://blog.blazingcdn.com/en-us/quic-protocol-internals-how-it-actually-works-on-the-wire",
  "publisher" : {
    "@type" : "Organization",
    "logo" : {
      "@type" : "ImageObject",
      "height" : 60.0,
      "url" : "https://143144902.fs1.hubspotusercontent-eu1.net/hubfs/143144902/Logo%20b-3.png",
      "width" : 245.85367
    },
    "name" : "CDN Blog"
  }
}
```