QUIC gets pitched as a speed upgrade over TCP, and that framing misses what actually happened. The real story is that TCP got stuck, frozen in place by the kernel it lives in and by decades of network hardware that learned to expect it to behave a certain way. QUIC’s design is best understood as a response to that problem, not a performance patch.

For decades, most web traffic followed a familiar stack:

UserspaceApplicationHTTPTLSKernelTCPIPNIChardware

TCP gives us things applications depend on every day:

  • reliable delivery
  • retransmissions
  • congestion control
  • flow control
  • packet ordering

And TCP is extremely good at this.

The problem was not that TCP stopped working.

The problem was that TCP became very difficult to change.


TCP became part of the infrastructure#

TCP transport logic normally lives inside the operating-system kernel.

That gives TCP excellent performance, but it also means changing the protocol is not like updating an application library.

Imagine introducing a new transport behaviour. You now potentially depend on every layer beneath your application agreeing to change at the same time:

ApplicationOperating SystemKernel TCP implementationNIC / OffloadsNetwork

And even if Linux supports your change, the Internet between two endpoints may not.

A typical connection today passes through infrastructure like:

ClientNATFirewallLoad BalancerProxyServer

Over many years, these devices learned how TCP usually looks.

Some inspect sequence numbers. Some inspect flags. Some expect particular handshake behaviour. Some simply reject traffic they do not understand.

So eventually something interesting happened: the network started depending on TCP’s existing behaviour.

This is called protocol ossification.

The protocol may technically be extensible, but changing it becomes risky because infrastructure in the middle has effectively frozen assumptions about how it should work.


QUIC avoided changing TCP#

Instead of trying to redesign TCP inside every operating system, QUIC took a very different path.

It runs over UDP.

Application
 HTTP/3
   QUIC
 ┌─────────────────┐
 │ Streams         │
 │ Reliability     │
 │ Flow Control    │
 │ Loss Recovery   │
 │ Congestion Ctrl │
 │ TLS 1.3         │
 └─────────────────┘
────────────── Userspace
   UDP
    IP
────────────── Kernel
   NIC

UDP still passes through the kernel. QUIC is not bypassing the kernel completely.

But instead of depending on the kernel’s TCP implementation for most transport behaviour, QUIC implements those mechanisms inside a userspace library. The diagram below shows the same layering as a flow, with the transport mechanisms QUIC now owns broken out explicitly:

ApplicationHTTP/3QUIC (userspace)implemented inside the QUIC libraryStreamsReliabilityLoss RecoveryCongestion ControlFlow ControlTLS 1.3UDP SocketKernel IP StackNIC

RFC 9000 defines QUIC as a secure, multiplexed transport protocol carried over UDP, and RFC 9308 specifically highlights an important consequence of this design: QUIC can be implemented in userspace and deployed over existing UDP-capable infrastructure.

And this changes something fundamental.


The transport layer can move at application speed#

Suppose a browser, CDN or proxy wants to change its QUIC implementation.

With TCP, major transport behaviour is closely tied to the operating system. With QUIC, much more of that logic can ship with the application itself.

QUIC v1 implementation
Application / library update
Improved QUIC implementation

You do not necessarily need to wait for every user’s operating system to replace its networking stack.

That makes experimentation much easier. Congestion control can evolve. Loss recovery can evolve. Packet scheduling can evolve. New protocol versions can be deployed.

Cloudflare is a good real-world example of this model. Its quiche project implements QUIC and HTTP/3 in userspace, meaning Cloudflare can actively modify and debug transport behaviour as part of its own software stack rather than treating transport as something completely fixed inside the kernel.

In May 2026, Cloudflare documented a congestion-control problem in its QUIC implementation where interaction between an idle-period optimisation and CUBIC’s congestion window could pin the connection at its minimum window and never recover, what it described internally as a QUIC “death spiral.” The interesting part is not the individual bug, it is that a CDN operator could inspect, modify and deploy fixes to transport behaviour inside its own userspace implementation.

That is a very different operational model from traditional TCP, where the same class of fix would have meant waiting on a kernel release and then waiting again for that kernel to reach production fleets.


UDP was also a deployment strategy#

This is the part that often gets oversimplified. People sometimes say:

That is not really the story. UDP gives QUIC something more valuable: deployability.

Networks already know how to route UDP. NAT devices already understand UDP. Firewalls already have UDP handling. Operating systems already provide UDP sockets.

QUIC’s approachThe road not taken
Build transport logic in userspace, over UDPDesign a brand-new transport protocol
Rely on UDP support that already exists everywhereRequire every firewall and NAT to add support for it
Deploy immediately on the existing InternetWait years for every OS to implement it

That would have made deployment dramatically harder.

Cloudflare’s production systems demonstrate this directly: its servers receive QUIC connections as UDP traffic, typically on UDP port 443, and the QUIC implementation handles the transport protocol above that layer.


QUIC also hides more from the network#

There was another lesson from TCP ossification. If intermediate devices can inspect transport internals, eventually some of them start depending on those internals.

QUIC deliberately exposes much less information. Much of its transport metadata is encrypted.

PropertyMiddlebox — TCP eraMiddlebox — QUIC era
VisibilityUnderstands transport stateSees only IP, UDP, and limited QUIC metadata
AssumptionsAssumes and depends on TCP’s behaviourCannot assume much — most fields are encrypted
PacketsModifies packets in flightCannot meaningfully modify packets
OutcomeEventually prevents protocol evolutionTransport behaviour stays controlled by endpoints

This makes troubleshooting and network inspection harder in some cases, but it also reduces the chance that today’s network devices accidentally prevent tomorrow’s protocol improvements.

That trade-off is intentional.


But moving transport to userspace has a cost#

This is where QUIC becomes more interesting technically.

TCP has had decades of optimisation. Operating systems and NIC manufacturers built features specifically around making TCP fast: TSO, GSO, GRO, checksum offload, mature socket paths, and NIC acceleration.

Now imagine implementing a sophisticated transport protocol largely in userspace. QUIC needs to handle:

  • packet encryption and decryption
  • acknowledgements
  • loss detection
  • retransmissions
  • congestion control
  • stream management
  • packet generation

At very high throughput, this can become expensive.

Cloudflare ran into exactly this class of problem. Its engineers noted that TCP had received years of software and hardware optimisation while UDP had historically received much less attention. Since QUIC runs over UDP, this initially placed QUIC at a performance disadvantage. Our own in-depth breakdown of QUIC vs TCP for inter-node traffic covers exactly why this happens at the syscall level: TCP offloads segmentation to the NIC via TSO/GRO and can move data with zero copies via sendfile(), while QUIC’s mandatory per-packet encryption forces that work back into the application process.

One technique Cloudflare explored was UDP Generic Segmentation Offload (GSO).

Without GSOWith GSO
One send() syscall per packetOne send() syscall for a whole large buffer
Many kernel transitions, one per packetThe NIC performs the segmentation into packets
Overhead scales with packet countOverhead stays flat regardless of packet count

Instead of userspace repeatedly sending many individual packets, the application can provide a larger buffer and let the lower networking layers perform segmentation. This reduces per-packet overhead and helps userspace QUIC recover some of the efficiency that TCP already enjoyed through mature kernel and hardware offloads.

So QUIC did not simply replace an old design with a universally faster one. It made a deliberate trade:

PropertyTCPQUIC
LocationKernel-integratedMore transport logic in userspace
PerformanceHighly optimised by decades of tuningRequires new performance engineering
EvolvabilityDifficult to evolveEasier to update and experiment with
Middlebox visibilityMiddleboxes understand a lotLess visible to middleboxes

And companies such as Cloudflare are effectively working on both sides of that trade-off: taking advantage of userspace flexibility while rebuilding some of the performance machinery that TCP accumulated over decades.


The bigger lesson#

QUIC is interesting because it solves more than a latency problem. It solves an evolution problem.

TCP became so successful that the Internet built infrastructure around its behaviour. That success made changing it harder.

QUIC’s answer was not:

It was closer to:

That is why QUIC’s architecture matters.

Sometimes moving something from the kernel into userspace is not primarily about making it faster. Sometimes it is about making the system possible to evolve again.

If you want the flip side of this story, the case for why you should still reach for TCP rather than QUIC inside a system you fully control, see The Userspace Tax Behind Protocols: QUIC(UDP) vs TCP , which walks through why etcd, CockroachDB, Kafka, and every other major distributed system still choose TCP for inter-node traffic.


References