Using Cloudflare's HTTP/2 Server Push

For those unaware, when using Cloudflare to proxy your site, HTTP/2 support is usually on by default, and highly recommended, and you can also enable HTTP/3 too. But, for a while I thought that Cloudflare’s HTTP/2 would rob you of one of the really cool and touted features of HTTP/2: the server push. Well, as it turns out, that’s completely possible. Let’s dive in.

HTTP/2 Server Push, Explained

The normal flow of HTTP has been, for a while now, you make a request, you get a response. The client is the one asking for resources, and the server is the one sending them. Besides the other benefits of HTTP/2, which I’m not explaining right now as that’s for another post, but I’ll explain it as this: HTTP/2’s protocol is a series of binary “frames” sent between the client and the server, usually with the client asking for a resource and the server sending it. However, the server may send the client a PUSH_PROMISE frame, in which it contains a valid request. Unless the client cancels this, the server will behave as if the request in that frame was sent to it by the client, and it’ll send the resource over. In effect, the server can synthesize “fake” requests to respond to, and then respond to them. When used correctly, this can allow for one to do things like push over resources like a page’s JS or CSS that it’s guaranteed to require, allowing those to download while the client is still reading through the main page to assemble it, thus saving time compared to the client needing to download a resource, then read it up until it sees that it needs something else, then grab that…

In most HTTP servers, it’s pretty simple to tell them when and what to push. Apache, for example, inspects the Link headers set in the response, so adding Header add Link… lines to a Location are what you’d use. With NGINX, you add http2_push directives in a location block. Caddy uses the push directive… they’re all pretty simple.

Telling Cloudflare What to Push

My initial thoughts told me that HTTP/2 would be nearly wasted with Cloudflare, since they will only use HTTP/1.1 to connect to your backend… I kinda forgot that their cache exists for a second. If all HTTP/2 traffic is being converted to HTTP/1.1, you’ve even lost the benefit of multiple streams per connection, but if you’re not, say, because cache, that’s better. But, that out of the way, if the backend can’t push to Cloudflare, how can Cloudflare push to clients? Well it turns out they did add a feature for this, and it’s one I kinda said just a bit ago: They inspect the Link headers of the response. Any resources given a preload relation in a Link header will be HTTP/2 pushed to the connecting client if they can. If the resources are in Cloudflare’s cache, they’ll get pushed over, otherwise they’re fetched, then pushed. I do find it odd that the Link headers are passed through anyways, but… either way, here’s an example:

Link: </theme.CSS>; rel=preload, </medium-zoom.js>; rel=preload;

And if you’ve not noticed, I’ve already done this, so any reports from your inspector should tell you that most CSS, JS, and even fonts that I’m using are getting pushed to you if they’re not already cached by your browser.

This header would, when sent to Cloudflare (or, Apache), tell it to start pushing the theme.css and medium-zoom.js files to the client.

If you’re curious, the Link header is a spin-off of the <link> HTML tag, which is used… to tell a client how various resources link to each other. Normally you’d find <link>s in the <head> of the HTML page, but if you add them as a header, then the client can see and act on those just a little bit faster. Also, unlike the tag, the header can contain multiple links at once.

The link tag has been used for ages to add style sheets to your page, which, at this point, are a necessity, but it can do more than that, depending on the relation. Link tags, if you’re not aware, look like this:

<link href="/theme.css" type="text/css" rel="stylesheet" />

They specify the resource (href), it’s MIME type (type, optional), and a relation (rel) to the current resource, but they can add other parts too.

Each relation can mean something different, preload usually means to preemptively connect and get ready to download, feed would be the website’s RSS feed, stylesheet is… a stylesheet, there’s an entire list.

103 Early Hints

If you’ve read the Apache documentation, you’d know about this one. HTTP’s 1xx codes are “informational” in that they don’t signify the end of a particular exchange, it’s just the server remarking about something. 103 Early Hints would be used to, say, send a bunch of Link headers to the client before the server has fully processed their request and is ready to send response data to the client, so the client can prepare by either getting ready to load the resources it needs, or accepting those PUSH_PROMISEs. While.. a Link header is just a Link header, the 103 Early Hints response was made pretty much entirely to send Link headers to the client for it to get to preloading resources while the server is still preparing a response.

I do find it funny though — If you send 103 Early Hints to Cloudflare’s servers, they have basically no idea what to do with that and… really do not like it. So uh… don’t do that.