Skip to content

The history of QUIC: from Google's 2012 experiment to RFC 9000

· 22 min read
Copyright: MIT
QUIC wordmark with an orange UDP arrow, over a black background

In 2012 a Google engineer wrote a long internal document that opened with an apology for its length. “If I had more time, I would surely have written less,” it said. The document proposed throwing out the part of the internet almost nobody questions: the assumption that a secure web connection rides on top of TCP. Nine years later that idea became four IETF standards, and the most cited of them, RFC 9000, defined a transport protocol that now carries a large share of the traffic between browsers and the biggest sites on the web.

The question worth asking is why it took nine years, and what changed along the way. The protocol that shipped as RFC 9000 in May 2021 is not the protocol Google deployed in Chrome in 2013. The name survived. Almost everything underneath it was rebuilt, most visibly the entire cryptographic handshake, which started as a Google-designed scheme and ended as standard TLS 1.3. This post traces that path.

We start with the latency problems that motivated QUIC and Jim Roskind’s original design. Then the gQUIC deployment across Chrome and YouTube, where Google ran the protocol on production traffic for years before anyone tried to standardize it. Then the handoff to the IETF in 2016, the decision to graft TLS 1.3 onto the transport, the split that produced HTTP/3, and the four RFCs of May 2021. The post ends on what the standardized protocol means for anyone watching traffic on the wire, with pointers to the HTTP/3 and QUIC fingerprinting work that picks up where this leaves off.

2012: the latency problems QUIC set out to solve

By 2012 Google had already shipped SPDY, the experimental protocol that became the basis for HTTP/2. SPDY multiplexed many requests over a single TCP connection instead of opening a fresh connection per resource. That solved one problem and exposed another. When you put every stream on one TCP connection, a single lost packet stalls everything, because TCP delivers bytes strictly in order and will not hand the application data past the gap until the missing segment is retransmitted. Roskind’s design document put a number on it: a delay of one packet pauses the entire set of SPDY streams. Multiplexing had moved head-of-line blocking from the application down into the transport, where it was harder to escape.

The second problem was the handshake. A fresh HTTPS connection in 2012 cost a TCP three-way handshake plus a TLS handshake on top, and the document noted the TLS round trip was a property of how TLS was implemented rather than anything security actually required. Round trips are expensive in a way bandwidth is not. You can buy more bandwidth. You cannot buy a faster speed of light, and the document said exactly that: round trip times, governed by the speed of light, will not diminish. Most web transfers are short. Short transfers are dominated by setup cost, so every round trip you remove from the handshake is a round trip the user feels.

The third problem was congestion control fighting multiplexing. When N streams share one TCP connection, one packet loss drops the whole connection’s send rate, where N separate connections would have dropped only one. The document estimated a single loss cutting throughput to eleven-twelfths of the pre-loss rate for a twelve-stream connection. Multiplexing onto one bytestream made loss more punishing, not less.

The clean fix for all three is to stop treating the connection as one ordered bytestream and start treating it as a bundle of independent streams, each with its own ordering, sharing one congestion-controlled path. TCP could not do that without kernel changes on every client, server, and middlebox on the internet, and TCP had ossified. Firewalls drop anything that does not look like TCP or UDP. NATs rewrite headers. Any new transport that wanted to deploy this decade had to look like one of the two protocols middleboxes already passed. Roskind’s document rejected the standardized option, SCTP over DTLS, on the grounds that it needed roughly four round trips to set up, and chose to build on UDP instead. UDP gets through middleboxes, and everything interesting happens in user space above it where Google could ship changes at the speed of a Chrome release rather than the speed of an operating-system upgrade cycle.

TCP: one ordered bytestream S1 lost S2 S3 S1 S2 and S3 wait for the retransmit QUIC: independent streams over one path S1 lost S2 S3 S2 and S3 deliver; only S1 waits A lost packet in TCP blocks every multiplexed stream. In QUIC it blocks only its own. *The core idea behind QUIC: move stream multiplexing below the application so a single packet loss no longer stalls unrelated streams.*

2012-2016: gQUIC, the version that shipped before the spec

What Google built next is now called gQUIC, the g for Google, to distinguish it from the IETF version that came later. It was not a paper protocol. The code went into Chromium and started serving real users, and that is the part of QUIC’s history people forget: the protocol spent years in production at scale before anyone wrote an RFC. QUIC first appeared in Chromium 29, announced on 20 August 2013, behind a flag. In early 2014 Google turned it on through Chrome’s experimentation framework for a sliver of users, under 0.025 percent, then widened it as the data came in. By January 2017 it was on for nearly all Chrome users and the Android YouTube app.

Running on production traffic at that scale produced numbers, and the numbers are the reason QUIC survived the trip to a standards body. Google’s 2017 SIGCOMM paper reported that QUIC cut Google Search response latency by 8.0 percent for desktop users and 3.6 percent for mobile, and reduced YouTube rebuffer rates, the stalls where the spinner shows, by 18.0 percent on desktop and 15.3 percent on mobile. By the time of that paper, QUIC accounted for over 30 percent of Google’s egress traffic in bytes, which the authors estimated at roughly 7 percent of all internet traffic. A single company’s experimental transport was already a measurable fraction of the global internet.

gQUIC gains, Google production traffic (2017) Search latency, desktop -8.0% Search latency, mobile -3.6% YouTube rebuffer, desktop -18.0% YouTube rebuffer, mobile -15.3% Bars scaled to relative magnitude. Source figures from the SIGCOMM 2017 paper. *The deployment numbers that justified standardization: lower search latency and far fewer video stalls, measured on live Google traffic.*

gQUIC did several things at once that the web stack normally keeps in separate layers. It carried its own encryption, using a Google-designed handshake called QUIC Crypto rather than TLS. That handshake aimed for zero round trips on a repeat connection by caching the server’s credentials, so a returning client could send request data in its very first flight, a property later generalized as 0-RTT. It identified connections by a connection ID carried in the packet, not by the four-tuple of source and destination IP and port, which meant a connection could survive a change of network. Your phone hands off from Wi-Fi to cellular, the IP address changes, and because the connection ID stays the same the session continues without a new handshake. It put loss recovery and congestion control in user space, where Google could iterate on the algorithm in a Chrome release instead of waiting for kernel upgrades to propagate across the installed base.

The other thing the production deployment bought was a lesson in how the internet actually behaves, not how the textbook says it should. Google’s paper reported that a meaningful fraction of clients could not use QUIC at all because the network between them and Google blocked or rate-limited UDP, so Chrome had to keep a parallel TCP connection racing the QUIC one and fall back the moment QUIC failed. Some networks throttled UDP specifically because it had historically been the carrier for floods and amplification, so a transport built on UDP inherited that suspicion. The protocol also had to be tuned against middleboxes that, having never seen QUIC, did unpredictable things to its packets. Each of those findings fed back into the design. Building in user space made that loop fast. A change to the congestion controller or the fallback logic was a Chrome release, measured in weeks, against the years a kernel TCP change takes to reach the installed base. That iteration speed, more than any single feature, is what gQUIC proved.

The version number churned through this period in a way that matters for anyone reconstructing the history. gQUIC was not one protocol but a sequence of wire-incompatible revisions, labeled Q035, Q039, Q043, Q046, and on, each shipped in a Chrome release and negotiated against Google’s servers. There was no expectation that an outside implementer would interoperate. The version was a private handshake between Chrome and Google’s front end, and it changed whenever the design did. That is the opposite of what a standard needs, where the wire format has to be stable enough for independent implementations to talk to each other across years. The gap between a fast-churning single-vendor format and a frozen interoperable one is exactly the gap the IETF process existed to close.

The catch was that QUIC Crypto was Google’s own design, reviewed by Google, deployed by Google. That is fine for a single-vendor experiment. It is not how the IETF blesses a transport that the whole internet is meant to implement and audit. When Google brought QUIC to the standards process, the crypto was the first thing to go.

2015-2016: the handoff to the IETF

Google submitted QUIC to the IETF as an Internet-Draft in June 2015, and a BoF, the informal “birds of a feather” session that gauges whether there is appetite to form a working group, led to the QUIC working group being chartered in 2016. The charter set the scope plainly: produce a standards-track specification for a UDP-based, stream-multiplexing, encrypted transport, based on the pre-standardization implementation and deployment experience Google brought to the table. The phrase “based on” did a lot of work. The working group was free to keep what worked and rebuild what did not, and it used that freedom.

The single biggest decision was encryption. Rather than standardize QUIC Crypto, the working group decided to use TLS as the basis of encryption in QUIC, on the reasoning that TLS had broad community support, a long review history, and the maturity that a one-vendor handshake could not claim. The timing was good. TLS 1.3 was being finalized in the same era, and TLS 1.3 had already cut its own handshake to one round trip with a 0-RTT mode of its own. So QUIC did not bolt on TLS as an afterthought. It was rebuilt around TLS 1.3 as the key-exchange engine.

How that integration works is more subtle than “QUIC runs TLS.” Early thinking ran TLS more or less whole, carrying well-formed TLS records inside a dedicated QUIC stream. The working group rejected that as wasteful, because it meant encrypting twice, once at the TLS record layer and once in QUIC’s own packet protection. The design that won pulls the TLS handshake messages out and carries them in QUIC’s CRYPTO frames, lets the TLS state machine produce the keys, and then uses those keys in QUIC’s own packet-protection scheme instead of the TLS record layer. TLS does the authentication and key agreement. QUIC does the encryption of packets. That division is the subject of its own RFC, 9001, and it is the reason a QUIC connection looks nothing like a TLS-over-TCP connection on the wire even though the cryptographic primitives are the same.

1-RTT QUIC handshake (TLS 1.3 inside CRYPTO frames) Client Server Initial: ClientHello in CRYPTO frame Initial + Handshake: ServerHello, cert, keys Handshake Finished + 1-RTT app data 1-RTT app data Initial packets are encrypted with keys derived from public values, so they are obfuscated, not confidential. *TLS 1.3 messages travel in QUIC CRYPTO frames; QUIC's own packet protection replaces the TLS record layer. A returning client with cached keys can send 0-RTT data in its first flight.*

The 0-RTT mode that QUIC inherited from both gQUIC and TLS 1.3 came with a security caveat the working group had to document carefully rather than wish away. Zero round trips means a returning client sends application data in its first flight, encrypted with keys derived from a secret cached from the previous connection, before the server has said anything in reply. The problem is replay. An attacker who captured that first flight can send it again, and the server has no fresh handshake input to tell the copy from the original. TLS 1.3 confronts the same issue, and the QUIC documents carry the same warning forward: 0-RTT data must be limited to requests that are safe to replay, which in practice means idempotent ones. A 0-RTT GET is fine. A 0-RTT request that moves money is not. The latency win is real and the constraint on what may ride in it is also real, and the spec is explicit that the application, not the transport, owns that decision.

There was also a small, almost philosophical fight over a single bit. QUIC’s short header has a fixed bit, sometimes called the QUIC bit, that early versions always set to a known value so middleboxes could recognize QUIC traffic. The working group came to see any field with a permanently fixed value as a future ossification hazard: middleboxes would start to depend on it, and the protocol would lose the freedom to ever use that bit for anything. The answer was greasing, the same technique TLS uses, where you deliberately vary values that have no fixed meaning so that no observer can safely assume they are constant. An extension lets endpoints negotiate that even the QUIC bit may be set to a random value, training the ecosystem never to rely on it. The whole approach is a reaction to TCP’s history, where every reserved field that anyone left predictable eventually became something a middlebox depended on, and the protocol calcified around it.

The working group also could not just inherit gQUIC’s header compression. HTTP/2’s HPACK assumes headers arrive in order, the exact guarantee QUIC deliberately gives up to kill head-of-line blocking. So a new scheme, QPACK, was written to compress header fields without requiring in-order delivery, trading a little compression efficiency for resilience against the blocking HPACK would have reintroduced. That detail matters for fingerprinting, because the QPACK and HTTP/3 settings a client advertises are part of what distinguishes one client from another, a thread the HTTP/2 and HTTP/3 fingerprinting work follows.

2018: the split that produced HTTP/3

Through the early IETF work, “QUIC” still meant one thing, a transport with HTTP baked into its assumptions, the way gQUIC had been. The working group untangled that. It separated the general-purpose transport, streams, packets, loss recovery, connection migration, from the specific job of carrying HTTP semantics over those streams. The transport became its own set of documents. The HTTP mapping became another. This is the modularization Google’s own paper had predicted, writing in 2017 that IETF standardization would break the monolithic protocol into constituent parts.

Once HTTP-over-QUIC was a separate document, it needed a name, and in late 2018 the HTTP and QUIC working groups agreed to call it HTTP/3. The naming was practical. HTTP-over-QUIC was the third major wire format of HTTP after HTTP/1.1’s text and HTTP/2’s binary framing over TCP, and calling it HTTP/3 signaled to operators and tooling that this was a peer of HTTP/2, not a side experiment. The decision landed in October and November 2018, and Daniel Stenberg, who had started writing an explainer for the protocol that February before it even had the name, had to retitle the whole thing.

The split is cleaner than it sounds, and it is the reason QUIC is more than HTTP/3. The transport defined by the QUIC RFCs is general. It does not know about HTTP. Other application protocols can map onto it, and DNS-over-QUIC and some media transports already do. HTTP/3 is one tenant of QUIC, the first and by far the largest, but the transport was deliberately built so it would not be the only one.

2021: RFC 9000 and its three siblings

The QUIC working group’s core output landed on 27 May 2021 as four RFCs published together. RFC 9000, “QUIC: A UDP-Based Multiplexed and Secure Transport,” is the one people cite, edited by Jana Iyengar of Fastly and Martin Thomson of Mozilla. It defines version 1 of QUIC: the packet formats, the stream abstraction, flow control, connection migration, and the connection-establishment logic. Alongside it, RFC 9001, “Using TLS to Secure QUIC,” specifies exactly how TLS 1.3 plugs into the transport. RFC 9002, “QUIC Loss Detection and Congestion Control,” documents the recovery and congestion algorithms. RFC 8999, “Version-Independent Properties of QUIC,” carves out the small set of header fields that must stay constant across all future QUIC versions, so middleboxes and load balancers have a stable thing to key on without the protocol ossifying around their assumptions.

Getting there took almost five years inside the working group, across 26 in-person meetings and 1,749 tracked issues, by Fastly’s count. The length was not bureaucratic drag. QUIC cut across three areas of protocol design that the IETF normally keeps in separate working groups, transport, security, and the application mapping, and decisions in one constrained the others. The work was contentious in the way work is when no single existing review process owns it.

QUIC, 2012 to 2022 2012design doc 2013Chromium 29 2015IETF draft 2016WG chartered 2018HTTP/3 named 2021RFC 9000 2022RFC 9114 *From an internal design document to a published standard took nine years; the protocol that emerged shares a name and little of its handshake with the 2012 original.*

HTTP/3 itself trailed by a year. RFC 9114, “HTTP/3,” edited by Mike Bishop, was published in June 2022, along with RFC 9204 defining QPACK. The HTTP documents waited on the transport documents to clear the queue first, which is why the protocol most people actually use, HTTP-over-QUIC in a browser, has a 2022 RFC even though the transport under it dates to 2021.

2021-2026: from standard to default

A published RFC is a starting line, not a finish. What happened after May 2021 is that QUIC moved from “the thing Google runs” to “the thing the major edges all run,” and the measurement of it split into two numbers that get confused constantly. One number is how many sites advertise HTTP/3 at all, which counts a site as adopting the protocol the moment it sends the Alt-Svc header or the HTTPS DNS record that says “I also speak h3 on UDP.” By that measure HTTP/3 support sits in the mid-thirties percent of websites as of 2026, dragged upward mostly by the CDNs, because Cloudflare, Fastly, Google, and the rest turned it on across their customer base and a single edge provider flipping a default moves the whole number. The other figure is how many actual requests arrive over HTTP/3, which is lower and noisier, because a client only uses h3 if it tried, the network allowed UDP through, and nothing forced a fallback to TCP. Cloudflare’s own request share has hovered around a fifth to a quarter of traffic in the years since, with the gap between “advertised” and “used” explained largely by networks that still block or throttle UDP and by clients that never attempt the upgrade.

Browser support stopped being the constraint quickly. Chrome shipped IETF QUIC and HTTP/3 by default to its stable channel, Firefox followed, and Safari enabled it. On the server side the protocol arrived through nginx’s HTTP/3 support and the QUIC libraries the big players open-sourced, so by the mid-2020s an operator turning on HTTP/3 was flipping a config flag rather than running an experiment. The interesting movement now is at the application layer above the transport. DNS-over-QUIC, defined in its own RFC, uses the transport directly. Media-over-QUIC is being worked on as a low-latency alternative to the streaming stacks built on TCP. The point of splitting the transport from HTTP back in 2018 was to let exactly this happen, and it is happening on a slower clock than HTTP/3 did because HTTP/3 had a billion browsers waiting for it and the newer tenants do not.

What the standardized protocol exposes on the wire

For anyone watching traffic rather than writing it, RFC 9000 changed the shape of what is visible. The whole motivation for building on UDP and encrypting aggressively was to deny middleboxes the purchase they had on TCP, the ossification the design set out to escape. QUIC encrypts not just the payload but most of the transport header. The sequence numbers, the acknowledgments, the frame types, the stream structure, all of it sits under packet protection. A passive observer sees UDP datagrams to port 443 and very little structure inside them.

Very little is not nothing, and the unencrypted remainder is exactly where fingerprinting lives. The first packet of a QUIC connection, the Initial packet, carries the TLS ClientHello inside a CRYPTO frame, and that Initial is encrypted only with keys derived from public, version-specific values. Anyone on the path can compute those keys and read the handshake. That is by design: the Initial keys obfuscate against dumb middleboxes, they do not provide confidentiality against a determined observer. So the ClientHello, with its cipher suites, its extensions, its ALPN value advertising h3, is readable, and it carries the same fingerprintable structure that makes TLS fingerprinting work over TCP. The long header of those early packets also exposes the QUIC version number and the connection IDs the endpoints chose, and the exact layout of fields a given client emits, transport-parameter ordering, packet sizing, the use of GREASE, varies between implementations in ways that identify them. The detail of which fields and how stable they are is the subject of the dedicated QUIC and HTTP/3 fingerprinting and QUIC initial-packet write-ups.

Connection migration cuts the other way. Because a QUIC connection is named by its connection ID and not by the IP four-tuple, the connection can move between networks, which is good for a phone changing networks and awkward for anyone trying to correlate a session to an IP address. RFC 9000 has the endpoints rotate connection IDs precisely so a passive observer cannot use a stable ID to link a connection across a migration. The protocol was built to be hard to track at the network layer. That property is load-bearing for the fingerprinting arms race, because it pushes identification up out of the IP layer and into the handshake bytes, where it meets the same TLS and HTTP signatures defenders were already collecting.

What the nine years actually changed

The throughline of QUIC’s history is that the hard part was never the idea. Multiplexed streams over an encrypted UDP transport with a fast handshake and connection migration: Roskind sketched all of it in 2012, and Google had it running on a measurable fraction of the internet by 2016. The hard part was making it something other than Google’s. That meant ripping out a working, deployed, Google-designed handshake and rebuilding the protocol around TLS 1.3, writing a new header-compression scheme because the old one assumed an ordering QUIC had thrown away, and splitting a monolith into a transport plus an HTTP mapping so the transport could outlive any single application. Almost five years of working-group time bought interoperability and a security story that a single vendor’s review could not provide.

What shipped is a protocol that hides most of itself. The encrypted transport header is the clearest statement of intent in the whole effort, a deliberate move to take back the flexibility that middleboxes had quietly stripped from TCP over thirty years. The parts that stay visible, the Initial packet’s readable ClientHello, the version field, the connection IDs before they rotate, are visible because deployability required it, not because anyone wanted them seen. That tension, between a protocol designed to be unreadable on the wire and the handful of bytes it must expose to get through the internet at all, is where every QUIC fingerprinting technique now operates. The history explains why those bytes are the ones that leak.


Sources & further reading

Further reading