One of my favorite Cloudflare Workers Sites features is the ability to use HTTP/2 server push to push and preload static assets like CSS, JS, images, and fonts. HTTP/2 Server Push is a feature that Netlify and Vercel don’t support at this time, so it really makes Cloudflare Workers Sites stand out for static site hosting.

What is HTTP/2 Server Push?

Before we dive into how how to configure HTTP/2 Server Push for a site hosted on Cloudflare Workers Sites, let’s quickly go over what Server Push even means. HTTP/2 Server Push allows for non-HTML assets like CSS, JS, images, and fonts to be pushed along with an HTML document.

This differs from the traditional way of serving HTTP requests where the HTML document has to be parsed before subsequent requests can be made to fetch CSS, JS, and other assets. Take a look at the request waterfall chart below which shows HTTP/2 Server Push in action.

HTTP/2 Server Push in action.

HTTP/2 Server Push in action.

After the initial HTML request, there are three static files that are pushed.

  1. https://brianli.com/css/style.min…css
  2. https://brianli.com/js/script.min…js
  3. https://brianli.com/js/instantpage.min…js

As you can see, there is almost no delay in serving these three static assets because the files are pushed to the client along with the HTML document!

Server Push vs. Preload

People often confuse HTTP/2 Server Push with Preload, and I don’t blame them! From the context of the current implementations of these two performance-enhancing technologies, it’s very easy to confuse them. The key difference between Server Push and Preload is that the latter still requires HTML to be parsed before any fetching of assets can take place.

How HTTP Preload Works

HTTP Preload instructions are placed in the <head> section of an HTML document using the syntax below.

<link rel="preload" href="style.css" as="style">

So, an HTML document with HTTP preloading might look something like this.

<head>
  <meta charset="utf-8">
  <title>HTTP Preload Example</title>
  <link rel="preload" href="style.css" as="style">
  <link rel="preload" href="script.js" as="script">
  <link rel="stylesheet" href="style.css">
</head>

<body>
  <h1>Some header here...</h1>
  <p>Some text here...</p>
  <script src="main.js" defer></script>
</body>

How HTTP/2 Server Push Works

HTTP/2 Server Push instructions are placed in the response headers of an HTTP request using the syntax below. Notice how the Server Push declaration also uses rel=preload. This is the primary cause of confusion between HTTP/2 Server Push and Preload – they both use the word “preload”.

link: </css/style.min.3172f3c2ff6d86148847bf6c39bff071.css>; rel=preload; as=style

Multiple assets for Server Push can be specified like this.

link: </css/style.min.3172f3c2ff6d86148847bf6c39bff071.css>; rel=preload; as=style, </js/script.min.26cc34bd5a07c08ad7237868ce404867.js>; rel=preload; as=script, </js/instantpage.min.b2f3edaed4c36f2285a5202b4823b89d.js>

From a performance perspective, HTTP/2 Server Push is typically superior to HTTP Preload because it doesn’t require the HTML document to be parsed before asset fetching can begin. Since relative links to assets are specified directly in the HTTP headers, a browser can immediately start downloading the assets as soon as the HTTP response is received.

With that said, as with most things in life, too much of something good can end up being bad – the same applies to HTTP/2 Server Push. Don’t push every single asset on your page. In general, it’s best to push critical assets only. On BrianLi.com, I only use HTTP/2 Server Push to deliver my main CSS and JS files.

How to HTTP/2 Server Push CSS

To push CSS files, use as=style.

link: </css/style.css>; rel=preload; as=style

How to HTTP/2 Server Push JavaScript

To push JavaScript files, use as=script.

link: </js/script.js>; rel=preload; as=script

How to HTTP/2 Server Push Images

To push CSS files, use as=image.

link: </uploads/image.jpg>; rel=preload; as=image

How to HTTP/2 Server Push Fonts

To push CSS files, use as=font/format and crossorigin.

link: </fonts/font.woff2>; rel=preload; as=font/woff2; crossorigin

HTTP/2 Server Push with Clouflare Workers Sites

BrianLi.com is currently hosted on Cloudflare Workers Sites (amazing performance), and I use the JavaScript below in my index.js for HTTP/2 Server Push. Pay careful attention to the syntax for each HTTP header entry!

'Link', 'Path to File; rel=preload; as=something'

This snippet appends a Link header to HTML responses (“text/html” content type), and uses rel=preload to specify the files for preloading.

const contentType = response.headers.get("Content-Type");

if (contentType.includes('text/html')) {
  response.headers.append('Link', '</css/style.min.3172f3c2ff6d86148847bf6c39bff071.css>; rel=preload; as=style')
  response.headers.append('Link', '</js/script.min.26cc34bd5a07c08ad7237868ce404867.js>; rel=preload; as=script')
  response.headers.append('Link', '</js/instantpage.min.b2f3edaed4c36f2285a5202b4823b89d.js>; rel=preload; as=script; crossorigin=anonymous')
}

After enabling HTTP/2 Server Push, you can use Chrome’s browser inspector to verify that it’s working correctly. In the screenshot below, you can see “Push/Other” in the “Initiator” column for the three assets listed above.

HTTP/2 Server Push in the Chrome browser inspector.

HTTP/2 Server Push in the Chrome browser inspector.

Do you use Cloudflare Workers Sites to host your static site? What are your thoughts on using HTTP/2 Server Push to improve performance? Let me know via email or reach out to me on Twitter!

Update (December 4, 2020): It looks like Google Chrome will be ending support for HTTP/2 Server Push in a future update. If and when that happens, the configuration discussed in this post will still work for HTTP Preload. Also, HTTP/2 Server Push should continue working in Safari and Firefox unless they remove support as well.