Using HTTP/2 Push on your web server
Introduction to HTTP/2 Server Push
The introduction of HTTP/2 has introduced a wealth of performance benefits, for most sites, without the website owner or developer having to make any changes at all. However one of the more interesting aspects, which will take some time to decide how to use correctly, is HTTP/2 Server Push. This allows the web server to push resources it thinks the browser will need to save time and that opens all sorts of opportunities (and dangers!).
Now there are some dangers to this. For example if you like this site so much, you might browse to another page. In this case you would have the css loaded (as I only use one css file across the whole site at the moment), but the webserver might still attempt to "save time" by pushing the CSS on to you again. This is a waste of everyone's time and resources and can actually mean that you've made performance worse. Browsers can see that the push is being initiated (as the website will send a special PUSH_PROMISE message) and say "No thanks - already got that" with a special RST_STREAM message, but in a lot of cases by the time that gets to the browser it might have already sent the resource, or at least have fetched it and queued it up, so is not ideal.
So using HTTP/2 does require some extra thought rather than blindly pushing every resource. How exactly this will be managed is very much up in the air at the moment, though cookies are one suggestion to state whether a resource already exists on the client - though of course cookies are independent of the cache and one or the other can be cleared with the other still being present so it's not a perfect solution. Robin Marx published and excellent in-depth post on HTTP/2 Push covering a lot more detail than I intend to cover in this basic intro.
How to set up HTTP/2 Push
Apache added HTTP/2 push support in 2.4.18 but at the time of writing this was not available in Nginx or IIS. CloudFlare have also been doing a lot of work in this area, and use a modified version of Nginx so I imagine it won't be long before it lands in there since CloudFlare have kindly open sourced their Nginx HTTP/2 code. Also Akamai have a beta for HTTP/2 Server Push capability.
Both Apache and CloudFlare push based on a Link: Header (Akamai uses a slightly different method) so this Apache config would push my stylesheet with every request (including the stylesheet itself!):
Header add Link "</assets/css/common.css>;rel=preload;as=style"
This could obviously be improved by only pushing this on .html resources (note this site is based on static .html file rather than .php files, but you can change the config as appropriate):
<filesMatch "\.([hH][tT][mM][lL]?)"> Header add Link "</assets/css/common.css>;rel=preload;as=style" </filesMatch>
However a better implementation would be to set a cookie, when the common.css file is pushed, and check that cookie next time, so you only push when the cookie is NOT set:
#Push the CSS file using HTTP/2 <IfModule http2_module> #Check if there's a cookie saying the css has already been loaded: SetEnvIf Cookie "cssloaded=1" cssloaded #If no cookie, and it's an html file, then push the css file #and set a session level cookie so next time it won't be pushed: <filesMatch "\.([hH][tT][mM][lL]?)"> Header add Link "</assets/css/common.css>;rel=preload;as=style" env=!cssloaded Header add Set-Cookie "cssloaded=1; Path=/; Secure; HttpOnly" env=!cssloaded </filesMatch> </IfModule>
This is fairly safe in that even if the css file is purged from the cache, then at worst the client will simply not benefit from push so no great loss. If the client disallows cookies completely (even first party session cookies), then it's a bit more damning as Apache will attempt to push the css file each time. It's a small file, and this is but a simple personal website so I'm OK with that as I'm really just experimenting for now, but larger sites should have a long hard think about these sorts of issues before enabling server push.
If you are using other technology behind Apache, to generate the pages (e.g. PHP or an App server such as Jboss, Tomcat, Node...etc.), then you can use them to set the headers appropriately, rather than Apache itself, and Apache should read those headers when sending the response and push appropriately.
When you load this page fresh in Chrome, with developer tools open (and with that cookie cleared) you should see the Initiator as "Push / Other" and also a very short download time, with no Green waiting time for the first byte:
As mentioned above, support of this is fairly light at the moment and most people who can benefit this will be using Apache, CloudFlare or Akamai. I'm not sure what other servers support this (nghttp2 does as this is the underlying code for Apache and Nginx as well as a few others) but do expect this list to grow.
On the client side, all the modern browsers but Safari support HTTP/2 Push. However they all treat a push as a push to cache, so only cacheable resources that would be used on the page anyway can be pushed. This of course also means that you need to ensure you are setting HTTP Caching Headers on these resources so when it's pushed, it's cached and then used by the page!
The three main downsides are: 1) Support is limited at the moment (see above), 2) There are dangers of over pushing and 3) It's more complexity! At the moment it's definitely an experimental feature and probably not something most sites should turn on, except perhaps for very simple use cases (like mine above). However it is definitely something to keep an eye on as it is one of the more interesting parts of HTTP/2 that is likely to lead to a lot of innovation in the future.
HTTP/2 Server Push is available now in some HTTTP/2 implementations and offers some interesting performance options (and dangers!) to address download times for websites.
Want to read more?
More Resources on HTTP/2
- My original HTTP/2 post.
- HTTP/2 Push: The details by Robin Marx published as part of the Annual Performance Calender series
- A Closer Look to HTTP/2 Push is an interesting post on this subject by Shimmercat.
- HTTP/2 Spec which introduces HTTP/2 Server Push, but left a lot of the details of how to do this out of the RFC.
- Addition of HTTP/2 Push to the Preload Link Spec
This page was originally created on and last edited on .Tweet