That's why content-driven websites should not be an SPA, and why I built https://mastrojs.github.io
There are a also ton of outbound requests for JS on first load.
[0]: view-source:https://http3-explained.haxx.se/
I can't find a link to the source anywhere.
(using a search engine is faster than asking for a link on HN)
I need fancy javascript crap like I need a hole in my head.
Looks unmaintained, though.
Edit:
> The initial QUIC protocol was designed by Jim Roskind at Google and was initially implemented in 2012, announced publicly to the world in 2013 when Google's experimentation broadened.
> Back then, QUIC was still claimed to be an acronym for "Quick UDP Internet Connections", but that has been dropped since then.
HTTP/1.0 was built primarily as a textual request-response protocol over the very suitable TCP protocol which guaranteed reliable byte stream semantics. The usual pattern was to use a TCP connection to exchange a request and response pair.
As websites grew more complex, a web page was no longer just one document but a collection of resources stitched together into a main document. Many of these resources came from the same source, so HTTP/1.1 came along with one main optimisation — the ability to reuse a connection for multiple resources using Keep Alive semantics.
This was important because TCP connections and TLS (nee SSL) took many round-trips to get established and transmitting at optimal speed. Latency is one thing that cannot be optimised by adding more hardware because it’s a function of physical distance and network topology.
HTTP/2 came along as a way to improve performance for dynamic applications that were relying more and more on continuous bi-directional data exchange and not just one-and-done resource downloads. Two of its biggest advancements were faster (fewer round-trips) TLS negotiation and the concept of multiple streams over the same TCP connection.
HTTP/2 fixed pretty much everything that could be fixed with HTTP performance and semantics for contemporary connected applications but it was still a protocol that worked over TCP. TCP is really good when you have a generally stable physical network (think wired connections) but it performs really badly with frequent interruptions (think Wi-Fi with handoffs and mobile networks).
Besides the issues with connection reestablishment, there was also the challenge of “head of the line blocking” — since TCP has no awareness of multiplexed HTTP/2 streams, it blocks everything if a packet is dropped, instead of blocking only the stream to which the packet belonged. This renders HTTP/2 multiplexing a lot less effective.
In parallel with HTTP/2, work was also being done to optimise the network connection experience for devices on mobile and wireless networks. The outcome was QUIC — another L4 protocol over UDP (which itself is barebones enough to be nicknamed “the null protocol”). Unlike TCP, UDP just tosses data packets between endpoints without much consideration of their fate or the connection state.
QUIC’s main innovation is to integrate encryption into the transport layer and elevate connection semantics to the application space, and allow for the connection state to live at the endpoints rather than in the transport components. This allows retaining context as devices migrate between access points and cellular towers.
So HTTP/3? Well, one way to think about it is that it is HTTP/2 semantics over QUIC transport. So you get excellent latency characteristics over frequently interrupted networks and you get true stream multiplexing semantics because QUIC doesn’t try to enforce delivery order or any such thing.
Is HTTP/3 the default option going forward? Maybe not until we get the level of support that TCP enjoys at the hardware level. Currently, managing connection state in application software means that over controlled environments (like E-W communications within a data centre), HTTP/3 may not have as good a throughput as HTTP/2.
Unfortunately, software implementations of QUIC suffer from dealing with UDP directly. Every UDP packet involves one syscall, which is relatively expensive in modern times. And accounting for MTU further makes the situation ~64 times worse.
In-kernel implementations and/or io-uring may improve this unfortunate situation, but today in practice it's hard to achieve the same throughput as with plain TCP. I also vaguely remember that QUIC makes load-balancing more challenging for ISPs, since they can not distinguish individual streams as with TCP.
Finally, QUIC arrived a bit too late and it gets blocked in some jurisdictions (e.g. Russia) and corporate environments similarly to ESNI.
EDIT: I found https://news.ycombinator.com/item?id=45387462 which is a way better discussion than what I wrote.
This would depend on how the server application is written, no? Using io-uring and similar should minimise context-switches from userspace to kernel space.
> I also vaguely remember that QUIC makes load-balancing more challenging for ISPs, since they can not distinguish individual streams as with TCP.
Not just for ISPs; IIRC (and I may be recalling incorrectly) reverse proxies can't currently distinguish either, so you can't easily put an application behind Nginx and use it as a load-balancer.
The server application itself has to be the proxy if you want to scale out. OTOH, if your proxy for UDP is able to inspect the packet and determine the corresponding instance to send a UDP packet too, it's going to be much fewer resources required on the reverse proxy/load balancer, as they don't have to maintain open connections at all.
It will also allow some things more easily; a machine that is getting overloaded can hand-off (in userspace) existing streams to a freshly created instance of the server on a different machine, because the "stream" is simply related UDP packets. TCP is much harder to hand-off, and even if you can, it requires either networking changes or kernel functions to hand-off.
Unfortunately, it’s not updated to include QUIC and HTTP/3 so I had to piece together the info from various sources.
How else would you consider "just" switching HTTP to UDP? There are minimum required features such as 1. congestion control 2. multiplexed streams 3. encryption and probably a few others that I forgot about.
They chose to keep the UDP layer because of its minimal overhead over raw IP and for better adoption and anti-ossification reasons, but conceptually, forget about UDP, QUIC is a TCP replacement that happens to be built on top of UDP.
Now for the answers:
- Why not HTTP over UDP? UDP is an unreliable protocol unsuitable for HTTP. HTTP by itself cannot deal with packet loss, among other things.
- Why not keep HTTP/2? HTTP/2 is designed to work with TCP and work around some of its limitations, it could probably work over QUIC too, but you would lose most of the advantages of QUIC
- Why not got back to HTTP/1? I could turn out to be a better choice than HTTP/2, but it is not a drop-in replacement either, and you would lose all the intersting features introduced since HTTP/2
Why doesn't HTTP/2 use more than one socket?
[1]: https://en.wikipedia.org/wiki/JSON_Meta_Application_Protocol
Publishing the spec for your proprietary API does not make it an industry standard.
https://http3-explained.haxx.se/en/quic/quic-connections#con...
But see the notes taken from the HTTP/3 RFC itself, written by the authors:
10.11. Privacy Considerations
Several characteristics of HTTP/3 provide an observer an opportunity
to correlate actions of a single client or server over time. These
include the value of settings, the timing of reactions to stimulus,
and the handling of any features that are controlled by settings.
As far as these create observable differences in behavior, they could
be used as a basis for fingerprinting a specific client.
HTTP/3's preference for using a single QUIC connection allows
correlation of a user's activity on a site. Reusing connections for
different origins allows for correlation of activity across those
origins.
Several features of QUIC solicit immediate responses and can be used
by an endpoint to measure latency to their peer; this might have
privacy implications in certain scenarios.
[0]: https://radar.cloudflare.com/adoption-and-usage#http1x-vs-ht...
With that said, I am 100% in agreement that the primary benefits of QUIC in most cases would be between client and CDN, whereas the costs are comparable at every hop.
On the Linux kernel [1], for some benchmark they average ~24 Gb/s for unencrypted TCP from kernel space with 1500-byte MTU using segmentation offload. For encrypted transport, they average ~11 Gb/s. Even using 9000-byte MTU for unencrypted TCP they only average ~39 Gb/s. So there is no inherent disadvantage when considering implementations of this performance level.
And yes, that is a link to a Linux kernel QUIC vs Linux kernel TCP comparison. And yes, the Linux kernel QUIC implementation is only driving ~5 Gb/s which is 20x slower than what I stated is possible for a QUIC implementation above. Every QUIC implementation in the wild is dreadfully slow compared to what you could actually achieve with a proper implementation.
Theoretically, there is a small fundamental advantage to TCP due to not having multiple streams which could allow it maybe a ~2x performance advantage when comparing perfectly optimal implementations. But, you are comparing a per-core control plane throughput using 1500-byte MTU of, by my estimation, ~300 Gb/s on QUIC vs ~600 Gb/s on TCP at which point both are probably bottlenecking on your per-core memory bandwidth anyways.
[1] https://lwn.net/ml/all/cover.1751743914.git.lucien.xin@gmail...
Go http webserver doesn't support http 3 without external libraries. Nginx doesn't support http 3. Apache doesn't support http 3. node.js doesn't support http 3. Kubernetes ingress doesn't support http 3.
should I go on?
edit: even curl itself - which created the original document linked above - has http 3 just in an experimental build.
nginx do support it.
It's not experimental when built with ngtcp2, which is what you will get on distros like Debian 13-backports (plain Debian 13 uses OpenSSL-QUIC), Debian 14 and onward, Arch Linux and Gentoo.
Reference: https://curl.se/docs/http3.html
caddyserver v2 supports HTTP/3 and it's an webserver written in go https://caddyserver.com/features
FYI: There is also an rust webserver which supports HTTP/3. https://v2.ferronweb.org/
https://www.haproxy.com/blog/how-to-enable-quic-load-balanci...