Skip to main content QUIC | IoT Worker

QUIC

When the same site becomes slower on a weak network, the problem is not always that the server is slow to compute. Sometimes time is being eaten by packet loss, retransmission, and head-of-line blocking in the transport layer. With HTTP/2, that becomes more visible: the application layer is already multiplexed, but the underlying transport is still TCP, so if one packet is lost, all the streams on that connection still wait together.

QUIC appeared in that background. It is not just “TLS moved onto UDP”. It reorganizes connection establishment, reliable transport, multiplexing, congestion control, and connection migration into one user-space transport protocol, and then runs HTTP/3 on top of it.

QUIC is not about being “faster because of UDP”. It is about moving secure handshakes, stream multiplexing, and connection migration into a recoverable, evolvable transport path without being locked to TCP’s header and kernel evolution pace

Why It Appears

HTTP/2 solved some old problems:

  • A single connection can carry multiple requests and responses
  • Header compression reduces repeated overhead
  • Browsers do not need to open many concurrent TCP connections the way HTTP/1.1 often did

But it still runs on TCP, so the limits still come from TCP:

  • Connection setup and TLS handshake are layered together, so the first request costs more latency
  • The connection is bound to a four-tuple, so network changes can break it easily
  • One lost packet can block later in-order delivery in the same connection
  • Transport capability depends on kernel and middlebox behavior, so upgrade speed is slower

QUIC is not trying to create “another faster HTTP”. It is trying to pull the transport bottlenecks Web traffic hits most often into a protocol layer that can keep evolving.

The Background It Came From

QUIC was originally pushed by Google and later standardized by the IETF into QUIC v1. That background matters because it explains QUIC’s style:

  • Built for public Internet and large-scale browser deployment
  • Emphasis on user-space implementation and fast iteration
  • Designed to land without replacing the entire Internet infrastructure first

Choosing UDP was not because UDP itself offers some advanced capability. It was because:

  • UDP is simple and widely deployable
  • The kernel and middleboxes have fewer fixed semantics around UDP
  • New transport behavior can live more inside the user-space protocol

QUIC does not give up reliability. It reimplements reliable transport and congestion control itself.

Grasp the Main Model First

QUIC can be understood in three layers:

  • UDP only provides best-effort datagram delivery
  • QUIC handles secure handshake, reliable transport, stream multiplexing, and connection migration
  • HTTP/3 is just the application protocol running on QUIC streams

You can compress the model like this:

Client
  -> QUIC Connection
    -> Stream 0
    -> Stream 1
    -> Stream 2

Two key points:

  • A QUIC connection is not simply the same thing as one IP + port four-tuple
  • QUIC streams advance independently, instead of sharing TCP’s single ordered byte-stream delivery boundary

The Most Common Main Path

The most common first-connection path is:

sequenceDiagram participant C as Client participant S as Server Note over C,S: RTT 1 C->>S: Initial[ClientHello] S->>C: Initial[ServerHello] + Handshake[EncryptedExtensions + Certificate + Finished] C->>S: Handshake[Finished] + 1-RTT[HTTP/3 Request] S->>C: 1-RTT[HTTP/3 Response]

In that path, QUIC pulls several things that used to be spread across layers into one place:

  • TLS 1.3 handshake data is carried in QUIC packet types
  • Once the handshake finishes, it moves quickly into 1-RTT encrypted transport
  • Application data can start flowing on independent streams very early

The engineering payoff is direct:

  • TCP + TLS 1.3 often needs 2-RTT before application data begins
  • QUIC + TLS 1.3 can often get that down to 1-RTT

That is not only “one less round trip”. For mobile networks and cross-region access, one RTT can be the difference between a visible and an invisible first screen.

Why QUIC Does Its Own Multiplexing

HTTP/2 already has streams, but those streams still sit on top of TCP’s single ordered byte stream. If TCP loses one packet, data that has already arrived cannot be delivered upward until the missing piece is filled in. That is transport-level head-of-line blocking.

QUIC moves multiplexing into the transport itself so each stream keeps its own offset and reassembly boundary. That means:

  • If stream A loses a packet, stream B does not have to wait for A’s already-received data
  • The application layer no longer has to fight TCP’s whole-connection in-order delivery as much
  • HTTP/3 can share one connection more stably across many concurrent requests

The tradeoff is clear:

  • QUIC itself has to implement stream management, acknowledgments, retransmission, and congestion control
  • The protocol is much more complex than “just use TCP”
  • Capture and debugging become harder because so much content is encrypted

So QUIC is not “a simpler transport”. It accepts higher implementation complexity in exchange for a better behavior model.

Why Connection ID Matters

TCP connections are naturally bound to the four-tuple: source IP, source port, destination IP, destination port. That works fine in fixed networks, but if the client moves from Wi-Fi to 4G, the four-tuple changes and the connection is easily treated as a new one.

QUIC introduces Connection ID so that “whose connection is this” can be separated from network addresses.

sequenceDiagram participant C as Client participant S as Server Note over C: Switch from Wi-Fi to 4G C->>S: QUIC packet on new path + original Connection ID S->>C: PATH_CHALLENGE C->>S: PATH_RESPONSE S->>C: Continue 1-RTT data

This solves two real problems:

  • The client’s network path may change, but the business connection does not have to be rebuilt
  • The server or load balancer needs a connection identifier that is more stable than the four-tuple

The tradeoff is also real:

  • The protocol needs path validation to prevent address spoofing
  • Connection ID generation and rotation need to balance routing and privacy

Many of QUIC’s seemingly “extra” design choices are the price of connection migration and public Internet deployment.

Why 0-RTT Stands Out in QUIC

QUIC also supports 0-RTT based on TLS 1.3 session resumption:

sequenceDiagram participant C as Client participant S as Server C->>S: Initial[ClientHello + PSK] + 0-RTT[Early Data] S->>C: Initial[ServerHello] + Handshake[Finished] + 1-RTT[Response] C->>S: Handshake[Finished]

For the Web, the appeal is strong because resumed connections can send requests earlier. But the constraints are the same as in the TLS discussion, and worth repeating:

  • 0-RTT data is replayable
  • It is only safe for idempotent requests
  • Whether 0-RTT is accepted is a server policy issue, not something the client can force

So when you see “QUIC supports 0-RTT”, the engineering question is not “everything is faster now”. The first question is whether the business semantics can tolerate replay risk.

What to Look At in Packet Capture

QUIC captures are easier to get lost in than TCP/TLS captures because you can see far fewer plaintext fields. The first things to look at are not all frame types, but these judgments.

First check the packet type and the phase

The most important packet types in a first connection are:

  • Initial
  • Handshake
  • 0-RTT
  • 1-RTT

They tell you directly which stage the connection is in. During troubleshooting, knowing whether you are still building the connection or already in application data is more useful than reading every field first.

Then check whether the connection is migrating

If the client network changes, focus on:

  • Whether the Connection ID continues across paths
  • Whether PATH_CHALLENGE / PATH_RESPONSE appear
  • Whether the data stream continues after migration

Many “weak network interrupted my request” cases differ only in whether the connection was rebuilt or the original connection completed a path switch.

Finally check which layer packet loss is really affecting

QUIC still loses packets, retransmits, and backs off congestion. It is not “immune to network problems”.

What is different is:

  • Packet loss still slows the whole connection
  • But it does not have to block delivery for all streams the way TCP can

So do not mistake “no whole-connection head-of-line blocking” for “packet loss barely matters”. It just narrows the blast radius from “all streams wait” to “smaller, more recoverable impact”.

What Engineering Should Actually Think About QUIC Today

  • Do not think of QUIC as “TLS on top of UDP”. It is already a full secure transport protocol in its own right
  • Do not reduce QUIC’s value to “one less RTT”. It really changes the behavior boundary of handshakes, concurrent streams, and connection migration
  • Do not blame every HTTP/3 behavior problem on the application layer. Many symptoms start with QUIC connection quality, loss recovery, and path changes
  • Do not treat Connection ID as a normal field. It is one of the prerequisites for connection migration and load-balancing behavior
  • Do not assume 0-RTT is always worth turning on. First check whether the business is idempotent, then check the server’s anti-replay policy

Further Reading

  • TCP - the ordered delivery and head-of-line blocking cost QUIC tries to avoid
  • Why TCP Congestion Control Makes Networks Slower After Loss - congestion control and path-quality problems QUIC still has to handle
  • UDP - the connectionless datagram layer QUIC sits on top of
  • NAT - why QUIC more easily runs into mapping timeout, path change, and public reachability boundaries
  • HTTPS - the TLS 1.2 / TLS 1.3 and HTTPS handshake main line
  • HTTP - HTTP semantics, intermediaries, and version evolution

References