Configuring HTTP2 Push with WordPress
Server Push allows you to push a set of files down to the browser along with the request for the HTML even before the HTML is parsed. This means sending things like logos and CSS before the browser knows to request them, and thus speeding up the perceived load time of your page.
Let’s say that you had a site that runs on PHP, which of course your WordPress instance does, and you wanted to use HTTP2 Server Push to push down two CSS files and your logo.
You can use the following code which will do just that for you…
<?php
header("Link: </css/vendor.css>; rel=preload; as=style", false);
header("Link: </css/styles.css>; rel=preload; as=style", false);
header("Link: </images/site/logo--red.svg>; rel=preload; as=image", false);
?>
This has to be at the very very top of the page being requested, so if you’re working with WordPress then you’re going to want to put it into your themes header.php
file. The URL of the asset you want needs to go within the brackets and you need to define the type of file you’re pushing as well.
The last argument, false
, comes from a PHP header replace function part of the request. By default, if there are two of the same headers the second one will overwrite the first one unless you include the false argument which then allows multiple headers of the same type to be sent.
The optional replace parameter indicates whether the header should replace a previous similar header, or add a second header of the same type. By default it will replace, but if you pass in FALSE as the second argument you can force multiple headers of the same type. – from PHP Header Manual
Push every time?
The nice thing about HTTP2 Server Push is that it will stop the request if it knows that the files are already cached on the browser, but it still starts the process of the request before this happens so you’re using up a little bit of your visitors battery and bandwidth in the process.
Instead what you would want to do is…
- Check to see if it’s the visitors first time to your site
- If so, HTTP Server Push your files down
- If not, just serve the regular page because the files will already be in cache
For this to work you need to work out who’s been to your site before and who is a first time visitor. The best approach that I know about when it comes to that is to set a cookie on the user and check to see if it’s there…. if it is they’ve been there before, and if not they haven’t.
Setting the Cookie
This requires a quick update to you functions.php
file. The first part creates the function to check to see if a cookie exists, and if not then set one that expires in 30 days.
The second part add_action
allows you to use the function in other PHP files.
function is_first_time() {
if (isset($_COOKIE['_wp_first_time']) || is_user_logged_in()) {
return false;
} else {
// expires in 30 days.
setcookie('_wp_first_time', 1, time() + (WEEK_IN_SECONDS * 4), COOKIEPATH, COOKIE_DOMAIN, false);
return true;
}
}
add_action( 'init', 'is_first_time');
Now you can use is_first_time()
in your PHP code as a mustard test.
Serving the right dish to the right person
Now that I’m setting a cookie and checking it (twice… gonna find out who’s naughty or nice), I can use that as a test and send something different based on the result.
The below code runs the test, and if it’s the first time we push the vendor.css, styles.css and the logo–red.svg file down using the code we looked at in the very first part of this article. You’ll notice that I’m also writing an empty div
with a first
or second
class… this is just for debugging purposes so that I know if the cookie is working as expect.
<?php
if (is_first_time()) {
header("Link: </wp-content/themes/rwd-is/vendor.css>; rel=preload; as=style", false);
header("Link: </wp-content/themes/rwd-is/styles.css>; rel=preload; as=style", false);
header("Link: </images/site/logo--red.svg>; rel=preload; as=image", false);
echo '<div class="first"></div>'; // Just here so I can see an update in the browser.
} else {
echo '<div class="second"></div>';
}
?>
In the second visit the CSS and SVG files will already be cached so they don’t need to be pushed anymore.
So, no more critical CSS?
That’s kind of up to you. The CSS file should come down at the same time as the HTML document, but looking at the waterfall charts it’s always a little bit afterwards. That might be fine, but if you’re already controlling whether or not to Push files with a cookie you can always extend that and include some Critical CSS as part of the HTML request and thus having the first paint even faster.
To do this I’ve included another if
statement to see if the user is on the home page (is_page(2)
), if so they get the Critical Home CSS, otherwise they get the rest of the sites critical CSS.
<?php
if (is_first_time()) {
header("Link: </wp-content/themes/rwd-is/vendor.css>; rel=preload; as=style", false);
header("Link: </wp-content/themes/rwd-is/styles.css>; rel=preload; as=style", false);
header("Link: </images/site/logo--red.svg>; rel=preload; as=image", false);
echo '<div class="first"></div>'; // Just here so I can see an update in the browser.
if ( is_page(2) ) {
get_template_part( 'template-parts/critical-css-home');
} else {
get_template_part( 'template-parts/critical-css-site');
}
} else {
echo '<div class="second"></div>';
}
?>
At the moment I’ve only got the two versions of the Critical CSS files, but over time I’ll split those out to target the different sections of the site (Articles, Examples, Snippets etc).
Any drawbacks?
Initially, I was pretty chuffed with this approach and it was working a dream on my local environment. First visits had 3 files pushed down along with Critical CSS, and the secondary visits had no HTTP2 Server Push or critical CSS.
Once I pushed it live I checked again and BAM, it still worked! The files were being pushed on the first view and the Critical CSS was there too! It wasn’t until I refreshed and got the same page again that I realised something was wrong.
After a few minutes I realised it was because the site sits behind Cloudflare which in turn acts like a CDN… well it is a CDN and that’s why I use it. Unfortunately, Cloudflare caches the first version of the page from the Origin Server which is always going to be the first visit version… and then because it’s cached it is then served to all follow up requests.
Now before you say, “Surely Cloudflare have taken this into consideration” the answer is YES, they have. At the moment I’m paying $25 a month to Cloudflare for this site, $20 for Pro and $5 for a dedicated SSL certificate. The COOKIE based caching that can be available in the Page Rules doesn’t kick in until you’re paying $200 a month for Business which is unfortunately out of my immediate budget.
This means that for now I have a choice either
- Keep using HTTP2 Server Push for every request and rely on the fact that the browser will kill the connection once it realises that I already have the file in cache
- Stop using HTTP2 Server Push for the files and go back to using Critical CSS for every page and lazy loading in CSS
- Pony up the $200 a month to allow caching via Cookies
- Stop using Cloudflare altogether
I haven’t decided yet.
Conclusion
Hopefully that provides you with a few ideas to get you going with using HTTP2 Push. If it’s sparked a few more ideas about how you can improve performance with H2 then please write about it and let me know, it would be great to feature more and different ways to approach these new tools we have.