HTTP/2 Server Push Explained

HTTP/2 Server Push is a new feature released along with HTTP/2 that allows compatible servers to push resources to the client before the client requests the resources. Instead of having to wait for the client to download the document, parse it, and send more requests to the server, the server just sends the documents it knows the client will need.

HTTP/2 allows a server to pre-emptively send (or “push”) responses (along with corresponding “promised” requests) to a client in association with a previous client-initiated request. RFC 7540

This allows sites to load much faster since it saves one round-trip between the client and the server. This optimization is basically free in terms of development work because everything is implemented at the protocol level.

What is Server Push?

Traditionally, HTTP clients like browsers connect to a server, request a page, receive the page, parse the page, then request any additional resources that the page requires. Any resources required to load the page require a separate request after loading the page even though they’re sent any time the page is requested.

Server Push improves this process by “pushing” resources to the browser before the browser even asks.

For example, instead of requesting the document, receiving the document, then requesting the resources, the client just has to request the document and receives the document and resources in the response.

In the example above, HTTP/2 Server Push increases page load time by 60 ms. This may not seem like much but Server Push improves load time by 25% by just enabling a simple configuration option.

How does it work?

This section will explain the technical details behind server push. If you don’t care about that, just skip to the next section.

HTTP/2 streams

Unlike HTTP/1, HTTP/2 is a binary protocol. Where HTTP/1 sends the normal GET / request in plain text, HTTP/2 uses binary encoding to save bytes. For example, HTTP/2 sends a HEADERS frame encoded in binary similar to the PUSH_PROMISE frame above and then it sends a DATA frame to send site data to the client.

HTTP/2 also comes with a new feature called streams. Streams allow multiple streams of frames to be sent over a single connection. This allows multiple requests/responses to be sent and received over a single connection. Whereas HTTP/1 supports only a single request/response at a time, HTTP/2 allows many requests/responses as long as there is enough bandwidth to send them.

This is a key feature for server push because server push involves sending multiple different resources at the same time.

HTTP/2 Server Push

First of all, the server and client must agree to support the feature. The client may disable server push by setting the SETTINGS_ENABLE_PUSH setting to 0 included in the HTTP/2 settings frame. If the client does this, the server should not push any resources to the client through Server Push.

After the client sends an HTTP/2 request to the server with SETTINGS_ENABLE_PUSH set to 1, the server will first send back a PUSH_PROMISE frame indicating to the client that it will be pushing some resources to the client. This includes a 31-bit stream ID and headers for the requested resource to provide context.

1
2
3
4
5
6
7
8
9
+---------------+
|Pad Length? (8)|
+-+-------------+-----------------------------------------------+
|R| Promised Stream ID (31) |
+-+-----------------------------+-------------------------------+
| Header Block Fragment (*) ...
+---------------------------------------------------------------+
| Padding (*) ...
+---------------------------------------------------------------+

The server sends these even before the page response so that the client doesn’t request any of the resources the server plans to push.

After sending the initial PUSH_PROMISE frame, the server then begins sending push responses on the streams identified by the stream ID sent previously in the PUSH_PROMISE frames. The client doesn’t send any requests for resources that it knows the server is pushing.

Clearly, HTTP/2 server push requires support by the server and the client, but the client can always fall back to HTTP/1. Next, I’ll discuss HTTP/2 Server Push support.

What browsers support Server Push?

HTTP/2 Server Push is supported on all major browsers (except for IE on Windows 7).

  • Firefox has supported HTTP/2 server push since February 2015.
  • Chrome has supported HTTP/2 server push since March 2015.
  • Safari as supported HTTP/2 server push since June 2015.

Clearly, HTTP/2 Server Push is very widely supported. It works on 95.5% of all users’ devices.

How much does it improve load times?

I ran a test on 500 of the top websites according to moz.com and I calculated the time spent loading the longest-loading resource from the server. This isn’t a perfect test since there are other factors that can marginally lower the benefit, but this should be a good estimate.

In some cases, over 1 second could be saved simply by enabling a configuration option in the web server. Generally, over 200 ms can be saved by enabling HTTP/2 server push.

Especially in cases where request chains are long, server push can be a huge help. For example, if you have HTML that loads CSS that loads a font, server push can decrease the latency of loading the CSS and font to 0.

You can use a tool like pagecheck to check request chains within your site.

How do I implement it?

HTTP/2 Server Push is implemented by most major web servers. Generally, you can add a reverse proxy in front of your site in order to still use your existing web framework, but also include some important features like Server Push or caching.

Apache

Apache supports HTTP/2 server push through a module called mod_http2. In order to enable this module, add this to your httpd.conf:

1
LoadModule http2_module modules/mod_http2.so

Then, you can enable HTTP/2 as a supported protocol from Apache by adding this line:

1
Protocols h2 h2c http/1.1

Make sure that SSLCipherSuite is configured with an HTTP/2 compatible cipher. You can also just leave it as a default value and it will be configured properly as long as you have an up-to-date version of OpenSSL. Don’t use any ciphers from this list as they are not supported by HTTP/2.

With Apache, you can push responses in one of two ways:

  1. Add Link headers to your application so the application notifies Apache of which resources need to be pushed. Link headers look like this:
1
Link </xxx.css>;rel=preload, </xxx.js>; rel=preload
  1. Add Link headers manually in your Apache server config:
1
2
3
4
<Location /xxx.html>
Header add Link "</xxx.css>;rel=preload"
Header add Link "</xxx.js>;rel=preload"
</Location>

All of the information from this section comes from the apache docs

NGINX

NGINX supports HTTP/2 server push similarly to Apache.

First, enable HTTP/2 support on your server by adding http2 to the listen directive:

1
listen 443 ssl http2;

Then, again you can either add http2_push directives to your application similar to above:

1
2
3
4
5
location = /demo.html {
http2_push /style.css;
http2_push /image1.jpg;
http2_push /image2.jpg;
}

Or, you can add a http_push_preload on directive to your server configuration which will respect headers sent from the application:

1
2
3
4
location = /app {
proxy_pass http://upstream;
http2_push_preload on;
}

All of this information and more can be found on NGINX’s blog post for server push.

Conclusion

HTTP/2 Server Push allows developers to decrease load times for free. Literally just enable a configuration option and set your application up to send Link headers and your application will load 25-50% faster.

HTTP/2 Server Push is supported by 95% of users’ browsers, so it should be a no-brainer for any site.

pagecheck allows you to track your load times across websites including front-end statistics which HTTP/2 would affect. If you’re interested or you have a use case we haven’t thought of yet, you can also send me an email here.

5 optimizations to make your page run faster

We all know the modern web has become extremely bloated. Everything takes too long to load and downloads a huge amount of data over the network which can be a big problem for user-experience.

In fact, the median page size in 2020 is 2 MB and steadily increasing.

Pages are getting bigger

Even worse, Time To Interactive, or the time it takes for a user to be able to interact with websites is at 10 seconds and growing higher over time. Imagine how much easier sites would be to use if that metric decreased by half.

This article will teach you how to use front-end performance metrics to evaluate how to optimize your page loading time.

Optimization 1: Load your content first

Many “progressive web-apps” or PWAs first load the HTML, which loads the Javascript, which loads the content (from an API). Obviously, if your site is designed like this, the time it will take for users to be able to use your site will be much higher than necessary.

A better system is to send the content of the page along with the HTML. This might sounds obvious, but it makes the site much more usable. Users aren’t really going to care if Javascript is loaded when the load a page, but they will care if the content isn’t loaded within a few seconds.

This can be done in a variety of ways, but the two simplest ways are:

  • Use server-side rendering to render your page initially.
  • Add something like this to your template for each page:
    1
    <script>var data = { ... };</script>

Then, when your page loads, users just have to wait for the Javascript to load which will already have the content for the page.

Optimization 2: Optimize images

Most sites that load slow usually have huge images that take a long time to fully load. There are a few optimizations you can make with respect to images, but the main ones are:

  • Use an efficient container like webp to store your images
  • Size images efficiently (don’t load a giant image if you only need a small one)
  • Load images lazily (instead of loading them on page load, load them after page load)

You can also use a service like (disclaimer: my service) PageCheck or Lighthouse to check for images that need optimizing.

Optimization 3: Don’t run any Javascript before window.onload

The browser runs all scripts before allowing page interaction, so if you have scripts that are run directly in a script tag, instead you probably should run them after the page loads.

For example:

1
2
3
4
5
<script>
// simulates a resource intensive script
var x = 0;
while (x < 10000) x++;
</script>

can be done much more efficiently as:

1
2
3
4
5
6
7
<script>
window.onload = function () {
// simulates a resource intensive script
var x = 0;
while (x < 10000) x++;
}
</script>

This ensures the page loads before you do some resource-intensive task.

You can also load scripts asynchronously which does basically the same thing:

1
2
3
<script src='/js/jquery.min.js' async></script>
<!--- or -->
<script src='/js/main.js' defer></script>

In short, wait until the page loads to do (almost) any scripting.

Optimization 4: Inline critical resources

It definitely does not make sense to inline all styles and scripts, but critical scripts and styles that are needed before the page is displayed should be inside <style> and <script> tags in the HTML document.

Of course, keep these as small as possible, only loading the critical parts as needed, but if your page is unusable or looks bad before certain CSS or Javascript is loaded, definitely send those resources along with the HTML document.

Chrome Dev Tools has a feature called Coverage that helps you identify which parts of your code are critical and which are not.

Chrome devtools coverage tab

Optimization 5: Support HTTP/2

HTTP/2 is a huge help in front-end performance. Instead of waiting for the browser to request resources after loading the HTML document, HTTP Server Push allows the server to send the browser resources while the HTML page is still being loaded.

Server push

HTTP/2 is supported by most modern browsers and is extremely easy to setup if you have an Nginx or Apache reverse-proxy in front of your application.

  • Nginx provides a guide to setting up server push here
  • Apache provides a guide for setting it up here

Conclusion

Despite the web getting more and more bloated, new technologies make speed-ups possible without eliminating code or changing much at all.

By focusing on getting the most important information to the web browser first, user experience can be improved for free.

Shameless plug: To track front-end performance, get recommendations on speed-ups, and audit your site for common security problems, you can check out my tool, PageCheck.

If you have feedback or a specific use-case I might be interested in, tweet at me or drop me an email and I’ll give you a free trial in exchange for feedback.

Thanks for reading!