HTTP/2 Server Push with NGINX, CloudFlare and WordPress

HTTP/2 Server Push with NGINX, CloudFlare and WordPress

With CloudFlare supporting HTTP/2 and Server Push, we can leverage that by using CloudFlare as a CDN even though NGINX does not support Server Push.

Setup CloudFlare

  1. Login / create an account at CloudFlare.com
  2. Make sure you use CloudFlares nameservers as your primary nameservers. Your domain must resolve using CloudFlares DNS server
  3. Go to DNS settings and create an A record pointing to your WordPress domain, e.g. blog.example.com
  4. Make sure to choose “DNS and HTTP proxy (CDN)” and not “DNS only”. Every requests to blog.example.com will go through CloudFlare which does its CDN magic. The added bonus is that everything runs under the same address so you save a DNS lookup to e.g. cdn.blog.example.com

Setup WordPress

  1. Make sure to disable your current CDN if using any. Since all traffic goes through CloudFlare now they will now automatically serve your static resources via their CDN network. I am using WP Super Cache for WordPress and it was as simple as unselecting a checkbox.
Be sure to disable your traditional CDN. Example shown for WP Super Cache for WordPress.

Setup NGINX

  1. NGINX needs to send a link header for each static resource to preload. Identify these using Chromes built-in developer tools.
Using Chrome to identify a websites static resources
  1. For each identified static resource to preload, add an add_header statement to your NGINX server block. Examples are given below for scripts, stylesheets and images.
    add_header link "</wp-includes/js/jquery/jquery.js>; rel=preload; as=script";
    add_header link "</wp-content/themes/twentyfifteen/genericons/genericons.css>; rel=preload; as=styles";
    add_header link "</wp-content/uploads/201604/image.jpg>; rel=preload; as=image";
    
  2. Restart NGINX, e.g. on Ubuntu type sudo service nginx restart

Notice that I could not get my fonts preloaded, if you have a working example of preloading a woff2 font, please leave a comment.

Test

The best way to confirm everything is working is downloading and installing Google Chrome Canary, the newest bleeding edge Chrome browser. Open developer tools, check disable cache and enter your website url. You should now see Push / ... in the Initiator column for each preloaded resource. If it does work try clearing your browser cache.

Example of resources delivered using HTTP/2 Server Push. Notice the difference compared to non Sever Push resources. It cuts about 100 ms from the timeline.

For me I see about 100 ms faster load of the resources I preload, longer if the network is slow. It might not look like much, but with a pageload around 1 second, that is still 10% faster pageload if you can do this for every resource. Or you might just have a single resource or two that takes much longer to load that you could Server Push. If you look closer at the header for the first line, the HTML document, you will see a f-h2-pushed header. This header confirms that CloudFlare took all the Link headers we send and pushed them out to the browser, e.g. example from my site:

cf-h2-pushed: </wp-includes/js/jquery/jquery.js>,</wp-content/themes/twenty...genericons/genericons.css>,</wp-content/themes/twentyfifteen-child/style.css>,</wp-content/themes/twentyfifteen/style.css>,</wp-includes/js/jquery/jquery-migrate.min.js>,</wp-content/custom/analytics.js>,</wp-content/themes/twenty...js/skip-link-focus-fix.js>,</wp-includes/js/comment-reply.min.js>,</wp-content/themes/twentyfifteen/js/functions.js>,</wp-includes/js/jquery/jquery.form.min.js>,</wp-includes/js/wp-embed.min.js>

If you see a link header for each resource you want to preload, CloudFlare did not do its magic.

Conclusion

An easy way to get a bit faster pageload if you can do this for all your resources or just the one really slow loading resource. It does require that the resources are hosted under your own domain so no external resources and that you use CloudFlare as a DNS and CDN. For now it works best on static resources you know each pageload needs, but for more dynamic content like images it works poorly. There are WordPress plugins for adding the preload tag for images, scripts and stylesheets, these however do not work when serving static html version of PHP pages since the PHP processor is never hit, only the first time. For me that means I need to disable WP Super Cache or just add the headers manually. Googles PageSpeed module for NGINX might at some point be able to handle it dynamic content better.

6 Comments

Jay

I have woff2 working, The problem I had was I had to add the following to pull the ver info off. First I added this to the themes functions.php. To pull of the ?ver on it and everything else.

function remove_cssjs_ver( $src ) {
  if( strpos( $src, '?ver=' ) )
    $src = remove_query_arg( 'ver', $src );
  return $src;
}
add_filter( 'style_loader_src', 'remove_cssjs_ver', 1000 );
add_filter( 'script_loader_src', 'remove_cssjs_ver', 1000 );

rename woff2 to woff ;)

Poul Serek

Hi Jay Thanks for the info. I am trying to get the above to work in NGINX since I am serving cached pages and they never invoke PHP. I still have trouble getting it to work, it does seem to send the asset when renamed to woff without the version info, but it will still ask for a woff2 variant and download that asset again. /Poul

Justin Avery

In the off chance that you don’t have access to the server you can send these headers through PHP. It took me a while to work out the best way to approach it so I wrote out a tutorial for it, hope it helps someone else too It took me ages to work out just how to do this using PHP, and with Wordpress as the CMS. I ended up writing up some detailed step by step instructions, I hope it helps someone else too https://responsivedesign.is/articles/configuring-http2-push-wordpress/

Poul Serek

Hi Justin

Excellent guide! I am in the same boat as you - a bit undecided if I should continue to use server push since I also switched to CloudFlare proxy and $200 per month seems a bit too expensive for my small site.

/Poul

Daniel G

I’m using push server for server files that contain a query string for cache buster purposes and keep track of changes, my nginx config for this in
/etc/nginx/sites-available/site.com is:

## Server push 
add_header link "; rel=preload; as=styles"; 
add_header link "; rel=preload; as=script"; 
add_header link "; rel=preload; as=styles"; 
add_header link "; rel=preload; as=script";

Is there a way to declare the asset link without the string value and make the server detect which asset to push on every mod? I mean for example, lets say I change /wp-content/themes/site/js/allv25.js?v170220b and in order to use cache buster ill change just the version in HTML TO

/wp-content/themes/site/js/allv25.js?v170306a

Is there any way to declare this and make the server push that asset?

Poul Serek

Hi Daniel

I don’t know of any way to do this dynamically, but you might be able to do it if using Lua or Javascript modules with NGINX. At least with the Lua module it looks like you can set the header dynamically and if you can scan a directory for files you can get all the filenames you need. This is what I would try first.

/Poul

Leave a reply