Video Summary and Transcription
HTTP 3, also known as H3, is the latest version of the HTTP protocol with new performance-related features. Enabling HTTP 3 requires minimal effort and provides significant benefits, but limits fine-grained control over performance features. Zero RTT has limitations due to security reasons and restrictions on allowed requests. Resource loading and prioritization in HTTP 3 have some problems, as browsers may not agree on resource importance. Fetch priority allows fine-grained control over resource loading order, and resource discovery can be improved with 103 Early Hints. Web transport provides low-level access to QUIC and HTTP3 features for real-time use cases.
1. Introduction to HTTP 3
Hello there, I'm Robin Marks and I work at Akamai. Today, I'd like to talk about HTTP 3, the latest version of the HTTP protocol. HTTP 3, also known as H3, has many new performance-related features that make it more efficient than H2 and TCP. Enabling HTTP 3 requires minimal effort and provides significant benefits. However, it also limits fine-grained control over performance features, such as the JavaScript Fetch API and the zero RTT feature.
Hello there, I'm Robin Marks and I work at Akamai. And I'd like to talk to you today a bit about HTTP 3, which is the latest and greatest version of the HTTP protocol.
And as you can see, it's really quite a bit different from HTTP 2. One of the big changes that happened was that we no longer use TCP underneath. But we've moved to a new transport protocol called QUIC instead. QUIC itself, which runs on top of UDP. You don't really need to know all of these details.
The main thing you need to know for today is that QUIC and H3 have a lot of new performance-related features on board, things that make it quite a bit more efficient than H2 and TCP, as such as help your web pages load quite a bit faster. Now, you might think, oh, this looks interesting, but it's probably going to be a lot of work for me, right, to start using all of these new features. Well, that's where the good news comes in. That's not true. Basically, if you just enable HTTP 3, which is often just the flip of a switch, you get all of these features out of the box. In fact, you don't even have to change anything about your web pages to make optimal use, as long as you're already tuned for HTTP 2, which, to be honest, after eight years, you really should be. This should work just fine on HTTP 3 as well. So, this is quite good. You can get all of the benefits with not a lot of work.
There is also a downside to this, however, because it means you don't get a lot of fine-gained control over these cool new performance features. A good example of this is the JavaScript Fetch API, of which you probably all know you really only have access to the top option. There is no way to say explicitly that you would like to use HTTP3 instead of HTTP2 for a call. This is something the browser itself decides based on some rather complex internal heuristics. So you can't pass like a protocol parameter or there's no fetch HTTP3. And thank God the last line here is not something we went for, not a specific HTTPS3 URL. That would have been absolutely crazy. Another example of this is the zero RTT feature. This is one of the core new performance features in H3 in that it makes the connection setup to be a bit shorter. So for H2 on TCP, this typically takes three individual round trips on the network before you start getting HTTP data back. Quick in H3 then lessen this to just two round trips because Quick can combine the transport and the cryptographic TLS handshake into one round trip. And then there is this magical new feature called zero RTT where you can already make an HTTP 3 request and get some response back in the very first round trip of the connection which is about the fastest we can do. This all sounds very good but again you really don't have a lot of control on whether or not, for example, zero RTT is used in let's say again a fetch call. There is no way to enable, disable this.
2. Zero RTT Limitations
The browser and server determine the zero RTT limitations for security reasons. It's often limited on many deployments and has restrictions on the types of requests allowed. The lack of control can lead to the browser sending incorrect requests, resulting in wasted potential. While you get the features for free, it's not without limitations.
This is again something the browser chooses for you. And in this case it's not just the browser, it's also the server that is going to be holding your hand. For some complex security related reasons, zero RTT is often limited quite a bit on a lot of deployments. So for example, the code that I have here says that you can only use get requests without query parameters in a zero RTT request. Which as you might imagine, really lowers the amount of use cases you can use this for. For example, it's kind of useless for most API related requests. And again, the thing, because you don't have a lot of control over this, it might be that the browser gets it wrong. Browser sends a wrong type of request in zero RTT. The server will then reject that and the browser will have to retry it after that first round trip. Kind of wasting the potential of zero RTT. So yes, you get all the features for free. But it's not all amazing as it might seem.
3. Resource Loading and Prioritization
I want to talk about how we load individual resources on a page and how we prioritize when those should be loaded. HTTP 3 uses a simple mechanism to assign priorities to resources, but there are some problems with this approach. Browsers may not agree on resource importance, and their prioritization may differ from what developers expect.
Now, of course, I don't want to just talk about things you can't do with H3. I want to talk about things you can do. So the browser kind of shields you from these internal details. The protocol is kind of a black box. But it does give you these somewhat higher level features that you can use to tune some of these behaviours. And I'd like to talk about some of these with you today.
The first one is going to be about how we load individual resources on a page and how we prioritize when those should be loaded. As you probably know, HTTP 2 and also H3 use only a single network connection to load all the resources. This means we need to somehow decide in which order the resources are actually loaded on that one connection. You might think of a very naive solution which is going to load them in the order that they appear in the HTML but that would be rather inflexible in practice. And you can also see that there are many different options in which you might decide to send these resources. It doesn't just have to be back-to-back sequential.
So what happens in practice? Who decides how this gets done? Well, this is of course the browsers. For every request that the browser makes it is going to assign what is called a priority. An indication of the importance of the resource when it should be loaded. At least in HTTP 3 this is a very simple mechanism. It's just an HTTP request header with a very predictable name – priority. You can even see this in the browser dev tools. A priority has just two parameters. An urgency parameter which is kind of just a number indicating the importance and then incremental saying if this resource can be mixed with data from other resources or not. Now the details are not very important for us today.
What is more important and interesting is that there are a few problems with this approach. First of all, the browsers don't necessarily agree on which resources should be most important. And secondly, even if they do agree, they might come up with a different solution than what you as a developer might intuitively expect. So let's look at some examples for this. So I looked at how the browsers did this prioritization a couple months ago. And here you can see the top line. All the browsers kind of agree that HTML is indeed a very high priority, kind of important for loading a web page. That's logical. Same for CSS, the bottom line here.
4. Browser Priorities for Fonts and JavaScript
The browsers have different priorities for fonts and JavaScript files. Firefox assigns medium priority to non-parser blocking JavaScript, while Chrome gives it a lower network priority. Safari assigns high priority to all JavaScript, except for async tagged files. Preloading JavaScript in Chrome always assigns it a high priority, which can be too high in some cases. A new feature called fetch priority allows fine-grained control over resource priority.
You can see that the browsers agree it's quite important. Though it's a little bit more important in Chrome than the other two. But things are very different for fonts. For example, Firefox really does not care about your custom fonts marketing department, which is kind of the opposite of what Chrome is doing, which assigns the highest possible priority to the fonts. So there are already big differences here.
Let's look at what they do for JavaScript, because JavaScript has a lot of different ways of loading JavaScript files. Let's look at Chrome and Firefox first. If you have a JavaScript in the head, which is parser blocking, so sync loaded, that's of course going to be a high priority in both. But then things differ. Then Firefox says for any other type of JavaScript, I don't really care, I'm just going to assign them all the medium priority. Chrome is a bit more fine-grained, it says, you know, if you tag these as async or defer, make them non-parser blocking, this is actually going to make them lower priority on the network as well. Now I could be wrong, but I guess that what you as a developer expect is probably what Chrome is doing, right? Async and defer are clear signals that these resources are a little bit less important than parser blocking JavaScript. So that makes some sense, but now hold on to your seats because let's look at what Safari does with this.
So in case it is not clear, Safari basically says, I don't really care where the JavaScript is mentioned in the HTML, I'm just going to give it all a high priority, doesn't matter if it's in the head or the bottom, it's all the same to me. The only exception is if you tag it as async, for some reason that gets printed down to the medium priority, which was kind of a surprise to me because I would have expected that to happen for defer, not for async. And I don't really know the reason why Safari is doing this. And this is already interesting, let's make it a bit worse. Let's introduce the preload option. What happens if you preload a JavaScript file? What you can see here is that for Safari and Firefox, it doesn't really matter much, they keep like their standard priorities for the preloaded resource as well. But for Chrome, it really depends on which JavaScript file you are preloading because it always assigns it a high priority. So let's say for example, if you preload a JavaScript that is tagged as async or defer in Chrome, you're actually considerably bumping its priority too high. Sometimes this might be something that you want. For example, a good use case there is like a cookie consent manager that we need in Europe where you don't want it to be parcel blocking. So you often tag it as async, but you do want it to be downloaded rather quickly to show the cookie pop-up to the users. In that case, this might be what you want, but I can also imagine quite a few other use cases where you don't want the priority to increase if you preloaded JavaScript. So we need a bit more fine-grained control. And luckily, recently a new feature was introduced that allows us to do just that. This is called fetch priority. And with that, you can kind of tweak the priority. You can't literally say one of those five levels but you can say make it a little bit more or a little bit less of what the browser would initially assign.
5. Fetch Priority and Resource Discovery
You can use fetch priority to control the loading order of different fetch calls. It's currently only available in Chrome but will be implemented in Safari and Firefox. Resource discovery can be improved by using 103 Early Hints, which allows the browser to discover resources before the HTML is generated.
So for example here if you preloaded defer you can actually say, I just want to preload it. I don't want to increase the priority. Keep the fetch priority low. And this is not just for JavaScript or preloads. You can also use this for example to load images. For example, your largest contentful painter hero image could be made a fetch priority high. And there are many other use cases. I would say read them on the web.dev link below on the slide.
Now one special case that I want to mention is again, the fetch API. Fetch is kind of weird because you can fetch many different kinds of resources. But internally the browser sees this as always the same type, always the type fetch. As you can see again, the browsers really don't agree on how important the fetch calls should be. Now this is kind of annoying because, let's say you have a complex JavaScript app where you have multiple parallel fetches at the same time. Some of those might be clearly of higher importance to your app logic than others. And so you might want to say those need to be loaded first, but you can't do this with apparently normal fetch. Luckily fetch of course also supports fetch priority. It's in the name, right? However, it's not called fetch priority. It's just called priority when you use it. And for example here you can actually lower the priority of some of your fetches. Should be really quite powerful if you do very fine grained resource loading logic. This is currently only in Chrome, but it's being implemented in both Safari and Firefox. So coming to a browser near you very soon.
But the second thing I wanted to talk to you about is resource discovery. Because of course we can only prioritize resources that we have identified as being needed. Now, usually, you know, how does the browser discover stuff? It's in the HTML. So if the HTML is slow to be generated, if we have a high time to first byte, the whole page load is also going to be slowed because the resources are discovered late. Wouldn't it be great then, if we could somehow do something like this, where we could actually have the browser discover the resources much earlier, even before any of the HTML comes in? Sounds magical, right? This is actually a feature that you can use in some setups, even today. This is called 103 Early Hints. Now, the setup I'm using here today, the example is that we are using an edge server, right? CDN something like CloudFlare or Akamai, or if you're very hip, Vercel or Netlify. This means that the browser will connect to the edge server.
6. Edge Server Fetching and Preloading
If the edge server doesn't have the HTML in cache, it fetches it from the origin. During this time, the edge server can send back 103 Early Hints, which is a list of links. The browser can then start preloading these files, so by the time the HTML arrives, the browser already has critical files. Preloading and preconnecting to third-party domains is also possible.
And if the edge server doesn't have, let's say, the HTML in cache, it needs to go fetch that from the origin. While that is happening, the edge server can send back to 103 Early Hints. And what this is, is really just a list of links. This is really, again, just preloads and preconnects. Usually you'd put those in the HTML, but of course, we don't have the HTML yet. We're waiting for it to be generated. So here, we just put these in the HTTP response headers directly. What then happens, the browser gets these links and it can then start preloading these files. So that by the time the HTML actually comes in from the origin, the browser should already have large parts of your most critical files. So this is what makes this magic happen, and it gets even better because you can even preload and preconnect to third-party domains as well.
7. Static Domain and Server-Side Rendering
You can open a new connection to a static domain and load more resources while waiting for the HTML. This relies on slow HTML generation, which is more common with server-side rendering. I suggest moving back to server-side rendering and dropping client-side rendering.
Let's say, for example, you have a static domain for which you used to host some assets like fonts. While everything else is happening, while you're waiting for the HTML, in parallel, you can already open a new connection to the other domain and load more resources. So this is really quite powerful, but as you can see, it really relies on having a rather slow HTML generation time, which is of course mainly going to happen if you use something like server-side rendering and not so much client-side rendering. So am I advocating that we all just move on mass back to server-side rendering, drop client-side rendering completely? Well, yes, that is exactly what I am suggesting. I am suggesting we all just go back to full PHP, forget all about the JavaScript. That's probably not what you expected to hear in a JavaScript conference, but what can I say? I'm old school.
8. Introduction to Web Transport
Web transport is a powerful option for real-time use cases, providing low-level access to QUIC and HTTP3 features. It allows you to choose congestion control algorithms, send unreliable datagrams, and access raw HTTP3 streams. When combined with upcoming web features like WebAssembly and WebCodecs, it enables efficient data processing and rendering. However, WebTransport may fall back to HTTP 2 if HTTP 3 is blocked, and the browser abstracts some details. Overall, HTTP 3 offers high-level features and exciting possibilities, including 103 Early Hints and Web Transport.
Now the last thing I wanted to talk to you about is something called web transport. I've talked a bit about the Fetch API, and that is usually most of what you need, but for some use cases, especially some are live real-time use cases, you need a bit more power. And up until now, you would have to use something like web sockets over TCP or if you really needed UDP or unreliable data, you could use something like the WebRTC data channel for this. Both work, but especially the latter one is kind of difficult to use. It's not very intuitive to really get set up, especially in a client to server context.
With HTTP3 and QUIC, we now get a third option in this list, which is called web transport. And I like to say, even though it's not completely correct, I like to say web transport is the closest we will ever get to a raw network socket in the browser. As you might know, there are no TCP or UDP sockets because of security reasons, but web transport exposes most of the low-level QUIC and HTTP3 features in a relatively easy-to-use way. So for example, web transport is not finished yet. This is the current design, it might still change, but it already gives you an idea of the powers that you might have. For example, you might even be able to choose the congestion control algorithm the browser would use, where you might tune for either high throughput or low latency. Similarly, something you see there is something called datagrams. You can actually send fully unreliable datagrams. These are not raw UDP datagrams. These are actually part of the QUIC connection, so they are fully encrypted and flow- and congestion-controlled, but they should still be very interesting for use cases like real-time gaming and media streaming. And finally, you have access to the raw HTTP3 streams using a very, I think, intuitive interface for anyone who has ever used other types of JavaScript streams.
So web transport is coming. It's not done yet, but you can test this out in Firefox and Chrome at this time. It really only begins to really shine, however, when you combine this with other upcoming and existing web features. For example, a lot of people are using this to reproduce the use case for WebRTC, live media streaming, but in a much more low level way where you get the data in through, for example, web transports. Then you can use something like WebAssembly to very efficiently process the data. Then there is something new called WebCodecs that actually allows you to decode or transcode the media data in a very efficient way directly from JavaScript or WebAssembly. You can then render it. And there are examples within a project called MediaOverQuick. They're working on a new protocol specifically for this that have some really amazing results for very low latency video right inside the browser without all the complexity of WebRTC. So WebTransport is really just a building block for many cool use cases on top.
Of course, there is always a catch. You might've seen this in one of the earlier slides. It's not raw HTTP 3 because some networks will actively block or disallow HTTP 3 in practice. So WebTransport will fall back to HTTP 2 if it's not available. And at least for now, the plan is to give you access to datagrams even on HTTP 2 even though they're not really unreliable. So, you know, something to watch out for again. The browser abstracts some of this sometimes a bit too much. With that, it's time to conclude this. I think it's clear that HTTP 3 is indeed a very powerful protocol. Even though you can't do much of it, there are some high-level features that you can use. Some of them are quite complex like prioritization and depend on the browser, but others should allow for many new, interesting use cases like for example, 103 Early Hints and especially Web Transport.
Comments