Automatically inline Critical CSS for your server-rendered React app with NGINX and ngx_pagespeed.

Getting critical CSS from a React application can be quite tricky, but luckily we can use NGINX as a proxy server with ngx_pagespeed to do the work for us (and get asset caching for free).


We will build NGINX with the 3rd party Google PageSpeed Optimization Library (ngx_pagespeed) which will automatically scan our HTML and CSS on page load and then inline our critical styles for us on subsequent requests.

This article assumes you’ve got an Ubuntu 16.04 (Xenial) server and a server rendered React (or any other JS framework) application ready to be served through NGINX. It also assumes you know how to reverse proxy a node server through NGINX. If you’re looking for a guide on how to set up server rendering with React you can read this article first;
https://hackernoon.com/hot-reload-all-the-things-ec0fed8ab0

Install “vanilla” NGINX

Sign in to your Ubuntu server and add the NGINX source package from Launchpad;

sudo add-apt-repository -y ppa:nginx/stable

Then we have to update apt to include our new source.

sudo apt-get update

And finally we install NGINX;

sudo apt-get install -y nginx

You should now be able to hit your server and see the default NGINX landing page (if not, try running service nginx start)
Great, we now have NGINX up and running! Let’s get to the fun part. Now we’re going to rebuild our NGINX installation from source and bundle in ngx_pagespeed, the NGINX version of Google’s mod_pagespeed.

First, let’s stop our running NGINX instance;

service nginx stop

Getting the ngx_pagespeed module

First, lets grab some dependencies we’re going to need;

sudo apt-get install build-essential zlib1g-dev libpcre3 libpcre3-dev unzip

Then lets make a folder for ngx_pagespeed;

mkdir /opt/pagespeed && cd /opt/pagespeed

Then we download it;

sudo wget https://github.com/pagespeed/ngx_pagespeed/archive/latest-beta.zip

And finally unzip and cd into the folder;

sudo unzip latest-beta.zip && cd ngx_pagespeed-latest-beta

Now we have to get the PageSpeed Optimization Library itself;

sudo wget https://dl.google.com/dl/page-speed/psol/1.12.34.2-x64.tar.gz

Let’s unpackage this as well;

sudo tar -xzvf 1.12.34.2-x64.tar.gz

We now have our PageSpeed module downloaded and ready to be built into NGINX. So let’s get on that!

Building NGINX from source

Let’s open our apt sources for NGINX, we have to make sure we get the include the sources for our NGINX package so we can customise our build;

Open and uncomment the deb-src link from this file;

sudo vi /etc/apt/sources.list.d/nginx-ubuntu-stable-xenial.list

The file should look like this (depending on your version, it should say trusty if you’re on Ubuntu 14.04);

nginx-ubuntu-stable-xenial.list

This line should be uncommented;

# deb-src http://ppa.launchpad.net/nginx/stable/ubuntu xenial main

Save the file and exit; esc + :wq! if you’re using Vim.

Then let’s update apt again;

sudo apt-get update

Now we have to install tools for package creation;

sudo apt-get install -y dpkg-dev

Then let’s create a directory for our custom NGINX build and cd into it;

mkdir /opt/custom_nginx && cd /opt/custom_nginx

Let’s get our NGINX sources;

sudo apt-get source nginx

And then we get our NGINX build dependencies;

sudo apt-get build-dep nginx

Now we’re at the home stretch. Let’s add our ngx_pagespeed module to our build rules. Open your NGINX source’s debian rules, the name of the folder it’s located in will depend on the current version of NGINX in the repo you downloaded, at the time of the article it is 1.10.3. List the contents of /opt/custom_nginx to find your version;

sudo vi /opt/custom_nginx/nginx-1.10.3/debian/rules

Add the ngx_pagespeed module to the configure flags for the full NGINX version;

--add-module=/opt/pagespeed/ngx_pagespeed-latest-beta

The configuration should look like this;

Then let’s go back to our top level custom NGINX directory;

cd /opt/custom_nginx/nginx-1.10.3/

Then let’s build our package. This might take a while. If you get a question during installation on whether to “Use the available Release binaries?”, just say Y.

sudo dpkg-buildpackage -b

When the build is finished we go up a level to our top level custom_nginx folder;

cd /opt/custom_nginx

There should be several NGINX debian packages in this folder now. 
Let’s install our custom full version (choose the version that matches your Ubuntu version);

sudo dpkg --install nginx-full_1.10.3-0+xenial0_amd64.deb

That’s it! Our custom NGINX build is now installed.

Upload your React app and set up your reverse proxy in NGINX to serve it. All that’s left to do once that is done is for us to enable PageSpeed in our NGINX config (I’m using the default config, but you should open whatever config file is serving your React application);

sudo vi /etc/nginx/sites-available/default

Inside the server block reverse proxying your React application add;

pagespeed on;
pagespeed FileCachePath /var/cache/ngx_pagespeed/;
location ~ "\.pagespeed\.([a-z]\.)?[a-z]{2}\.[^.]{10}\.[^.]+" {
add_header "" "";
}
location ~ "^/pagespeed_static/" { }
location ~ "^/ngx_pagespeed_beacon$" { }
pagespeed RewriteLevel PassThrough;
pagespeed EnableCachePurge on;
pagespeed PurgeMethod PURGE;
pagespeed EnableFilters prioritize_critical_css;

Then save and exit the file. Make sure the pagespeed configuration is pasted above the first location block inside the server block.

Then we just have to create our cache folder;

sudo mkdir -p /var/cache/ngx_pagespeed

and finally restart NGINX;

service nginx restart

After the second visit to any route in your application PageSpeed will now automatically serve critical CSS inlined in a style tag in the header of your HTML response. It does this by inlining a javascript snippet that beacons back to your server with the information about your html / css that it needs. As a bonus it also defers loading the css file itself.

There’s tons of optimisation filters available in PageSpeed, we have disabled everything but the critical css filter. We’ve also added a way to invalidate the PageSpeed cache. I suggest reading up on the available filters (and handling PageSpeed cache), there’s a lot more it can do to improve your applications performance and load time.

Further reading;
https://modpagespeed.com/doc/build_ngx_pagespeed_from_source
https://modpagespeed.com/doc/filters