Taking advantage of HTTP/2 push can bring big improvements to front end performance. This pushes resources to the browser preemptively, avoiding extra requests and waiting time. I’m going to cover how to do this in WordPress.
This has made big improvements to page load times, removing connections and waiting times for resources, as well as doing transfers in parallel.
Preloading Files
To do this, I used HTTP headers instructing the browser to preload
resources. This isn’t http/2 push, but it directs the browser to fetch things in advance while the initial request is still transferring. The browser can then request all of them at once in parallel. For example:
header("Link: </wp-content/themes/tomjn/style.css?ver=5>; rel=preload; as=style", false);
To start with, I did this manually in functions.php
, and it helped in particular with Google Analytics and Typekit.
Actively Pushing Files With Nginx
To get Nginx actively pushing data to the browser, I added the http2_push_preload on;
directive to my sites config, and reloaded Nginx. This pushes any resources with relative URLs in the http header taking the Link: </...>; rel=preload
format mentioned above. It only works for relative URLs on the same domain though, falling back to the previous behavior for Typekit and other external resources.
The nghttp
command can be used to confirm this is working:
❯ nghttp -ans https://tomjn.com
***** Statistics *****
Request timing:
responseEnd: the time when last byte of response was received
relative to connectEnd
requestStart: the time just before first byte of request was sent
relative to connectEnd. If '*' is shown, this was
pushed by server.
process: responseEnd - requestStart
code: HTTP status code
size: number of bytes received as response body without
inflation.
URI: request URI
see http://www.w3.org/TR/resource-timing/#processing-model
sorted by 'complete'
id responseEnd requestStart process code size request path
13 +20.86ms +644us 20.22ms 200 3K /
2 +44.97ms * +19.24ms 25.73ms 200 33K /wp-includes/js/jquery/jquery.js?ver=1.12.4
4 +50.87ms * +19.30ms 31.57ms 200 3K /wp-includes/js/jquery/jquery-migrate.min.js?ver=1.4.1
6 +51.09ms * +19.32ms 31.77ms 200 3K /wp-content/uploads/2016/11/favicon.png
8 +53.43ms * +19.33ms 34.10ms 200 4K /wp-includes/js/wp-emoji-release.min.js?ver=5.0.3
10 +53.56ms * +19.36ms 34.20ms 200 753 /wp-includes/js/wp-embed.min.js?ver=5.0.3
12 +53.68ms * +19.36ms 34.32ms 200 639 /wp-content/plugins/jetpack/_inc/build/widgets/milestone/milestone.min.js?ver=20160520
14 +54.32ms * +19.38ms 34.94ms 200 4K /wp-includes/css/dist/block-library/style.min.css?ver=5.0.3
15 +63.11ms +20.91ms 42.20ms 200 12K /wp-content/plugins/jetpack/css/jetpack.css?ver=6.9
16 +63.88ms * +19.39ms 44.49ms 200 14K /wp-content/themes/tomjn/style.css?ver=5
18 +82.97ms * +19.41ms 63.56ms 200 27K /wp-includes/css/dashicons.min.css?ver=5.0.3
Files transferred with a *
were sent via HTTP/2 push.
Automating Pushes in WordPress
To avoid needing to keep my preload headers up to date, I explored auto-preloading enqueued assets. This plugin tries to automate sending headers for enqueued scripts:
https://gist.github.com/tomjn/7fe22a4ec20f2565004bd216e9d1f497
It acts at the last moment of the template_redirect
action, but this means only a subset of scripts and styles are caught. Sadly, it’s not possible to do this with the print styles actions. In the future I might experiment with output buffering to delay the first byte long enough to run on this script.
Pushing inline scripts would also need full page output buffering. This would kill performance though in the same way that Autoptimize destroys time to first byte.
do you have twitter? I want fo follow your brain dump
https://twitter.com/tarendai!