RFC 9114 published last month, which makes HTTP/3 a standard rather than an experiment. This is about reading it as a transport change rather than an HTTP one: congestion control and loss recovery moved out of the kernel and into the process serving your requests. That pays on a lossy phone link, pays close to nothing inside a fast network you already own, and costs you the packet capture either way.
9 July 2022·7 min read·networking
Everything before this ran on TCP, which means everything before this ran on code in the kernel. Congestion control, retransmission, ordering, the receive window, the timers: all of it maintained by people who are not you, shared by every process on the machine, upgraded when the operating system is, and offloaded onto the network card wherever the card can be persuaded to help. Your web server handed a byte stream to a socket and stopped thinking about it.
HTTP/3 runs on QUIC, and QUIC runs on UDP, which delivers a datagram or does not and has no notion of a connection at all. So all of that work is done again, in the process serving the request, by a library linked into your web server. The TLS handshake came too, interleaved with the transport handshake rather than layered on top of it. RFC 9000 standardized the machinery in May last year and RFC 9114 finished mapping HTTP onto it last month.
Which is the whole piece in a sentence. HTTP/3 is not faster HTTP. It is the same HTTP semantics with the transport carried across the boundary between the kernel and your process, and every advantage and every cost that follows is a consequence of that one move rather than of the version number.
There is one handshake instead of two. A new TLS connection over TCP costs a transport round trip and then a cryptographic one before any request moves. QUIC folds them together, and a client resuming a session can attach data to its first packet and pay nothing. The size of that win is proportional to the round trip time and to how often you open connections, which makes it enormous for a browser reaching a distant origin and negligible for a pool that connected at startup.
Loss stops stalling everything behind it. HTTP/2 multiplexes many streams onto one TCP connection, and TCP is a single ordered byte stream, so one lost segment holds up every stream whose bytes arrived after it. QUIC tracks streams separately and delivers each as soon as it is whole. The benefit scales with the loss rate, which on a wired path is near zero and on a congested mobile link is not, so the same change is transformative for one user and invisible to another.
The connection survives changing networks. A TCP connection is identified by its four-tuple of addresses and ports, so a phone leaving wifi for cellular loses every connection it had. QUIC identifies connections by an opaque connection ID carried in the packet, so the address can change underneath and the session continues. There is no TCP equivalent and no way to add one, and for anything with a mobile app this is probably the largest single item on the list.
Look at what those three mechanisms fix: long round trips, real packet loss, and clients whose address changes mid-session. Now describe the inside of a data center. Round trips in tens or hundreds of microseconds, loss near zero on a healthy fabric, connections long-lived and pooled so the handshake amortizes to nothing, endpoints that stay exactly where they are. Every premise is absent, and a fix for an absent problem is just its own cost.
That cost is measurable. Google's SIGCOMM 2017 paper reports serving traffic over QUIC initially taking about 3.5 times the server CPU of TLS over TCP, falling to roughly twice after a hand-optimized cipher, a memory-mapped kernel receive ring and cache-conscious rewrites of the hot paths. Twice, after that work, by that team. The same paper is where the good numbers live too, search latency down 8.0 percent for desktop users and video rebuffering down 18.0 percent, and all of it is Google's own pre-standard QUIC measured in 2016 on Google's clients and Google's network. It is evidence the mechanisms work, not a forecast of what you will collect.
The same paper says the quiet part directly. On networks with plentiful bandwidth, low delay and low loss, QUIC showed little gain and occasionally a negative impact, and it names the conditions: over roughly 100 Mbps, or a round trip of a few milliseconds. That is a description of a competent internal network, published by the team with the most to gain from the opposite finding.
So the framing I would resist is HTTP/2 against HTTP/3, because nobody actually makes that choice. The question is which segment of the path you are describing. One request can arrive over HTTP/3 from a phone on a train, be handed to an origin over HTTP/1.1 on a socket, and be right in both places. Enabling a protocol end to end because a version number went up is picking a transport for a network you do not have.
Some networks simply do not pass it. Measuring video clients in late 2016, Google found QUIC succeeded for 95.3 percent of those that tried it, and that 4.4 percent could not use it because UDP was blocked or the path could not carry the packet size. Those users were commonly on corporate networks behind enterprise firewalls. If your audience sits at desks inside other companies, a real share of it will never reach you over HTTP/3, and the TCP path is permanent rather than transitional.
And some pass it badly, which is worse. A further 0.3 percent were on networks that appeared to rate limit UDP, detected as elevated loss and reduced bandwidth at peak hours rather than as an outright failure. Packet size is now your problem too: unreachability climbed sharply once the total packet exceeded the 1500 byte Ethernet MTU, so Google settled on a 1350 byte payload, a permanent tax on every tunnel and VPN that shaves the path.
Falling back is the design, not the failure. A server advertises HTTP/3 in a header and the client tries both, and Google's implementation raced them by delaying the TLS over TCP attempt up to 300 milliseconds. That is a good design with one consequence people skip: you never turn TCP off. You operate two transports permanently, with two sets of tuning and two sets of failure modes, and the second is not a legacy path anybody is draining.
A great deal of operational practice is shaped by TCP being in the kernel and being legible. You capture on port 443 and read the handshake, the sequence numbers, the retransmissions and the window. The kernel keeps counters and a connection table, so ss tells you what is open and how it is doing. The firewall tracks connection state because there is state to track. None of that was designed; it accumulated, and it is what people reach for at three in the morning.
QUIC keeps almost none of it visible. RFC 8999 defines the small set of properties an observer may rely on across QUIC versions, and it is deliberately close to nothing: enough to recognize a packet and route it, not enough to understand it. A capture gives you UDP datagrams between two addresses, with their sizes and timings, and that is the whole of it. The kernel goes quiet in the same way, since the connection is not in its TCP table and the retransmit counters on your dashboards are still faithfully counting a protocol that path stopped using.
The replacement is real but it is not equivalent. The endpoint has to report on itself, through connection-level logging from the QUIC library or a key log fed into a decrypting capture on a machine you control. That works, and it is how the people who build these stacks debug them. It is also a different arrangement of trust: the thing you are investigating is now the only witness, and when it is the thing that is broken you have lost the independent view you used to get for free.
Enable it at the edge, and stop there. The client-facing terminator is where the round trips are long, the loss is real and the addresses change, so that is where every mechanism in the protocol earns its keep. Inward from there, speak whatever is cheapest and most legible on a path with microsecond latency. That is not a compromise position. It is putting the protocol where its assumptions hold, which is the only place any protocol is any good.
Treat TCP as permanent, not as a fallback. Given a measurable share of clients who cannot pass UDP at all, the TCP listener stays on the critical path indefinitely and deserves the same tuning, monitoring and care during an incident. The failure I would expect is a team that enables HTTP/3, quietly reclassifies TCP as legacy, and discovers during an outage that the configuration nobody has touched in a year is the one serving every customer behind a corporate firewall.
Audit the tools, then graph the fallback rate. Write down what you actually reach for during an incident and check each item against a UDP flow: the capture, the flow logs, the firewall rules, the access log fields, anything inspecting traffic on the way in. Then watch the share of clients that were offered HTTP/3 and ended up on TCP anyway, which is the only honest measure of whether this is working and is almost never on a dashboard.
I want to be clear that this is not an argument against the protocol, which is good engineering solving problems TCP could not be fixed to solve, because TCP cannot really be changed any more without the middleboxes objecting. If you serve mobile clients over the public internet, turn it on at the edge. The migration behavior alone justifies it, and the fallback path means the downside of being wrong is bounded and reversible.
What stays with me is the direction of the move rather than the protocol doing the moving. Work that lived in the kernel is linked into the application now, responsibility moved with it, and the observability followed the code rather than staying with the operator. A capture on port 443 used to be a conversation you could read line by line. It is a wall of encrypted datagrams now, and whatever it is doing wrong is being reported only by the thing doing it.