Brotli is gaining steam as the compression algorithm du jour for high performance websites. Created back in 2013 by Google to decrease the size of WOFF files, Brotli was standardized in 2016 as part of RFC 7932. The sales pitch for Brotli is better compression than Gzip - with similar CPU usage. Better compression leads to faster performance, but how much better is it?
To find out, we need to enable Brotli compression. Gzip has been available in NGINX for some time. It’s a bit fiddly to ensure you’re compressing all the appropriate mime types, but mostly it’s easy to setup and get going.
Unfortunately, Brotli is not part of the default NGINX package at this time, so you have to load it as a dynamic or static module. The Brotli NGINX module is currently developed and maintained by the folks at Google.
NGINX Plus has official support for the Brotli module. They’ve done all the hard work of packaging the module in to the NGINX Plus repository for you. It’s just an
apt-get install away! That is - if you’re a paying customer.
Since we’re cheap, we don’t have access to the NGINX Plus repository. We only have the open source version of NGINX, which means we need to load the module by hand. The process is roughly this:
- Install a load of pre-reqs
- Compile the module for your system (groan)
- Configure NGINX to use the newly compiled module
Even though you’ve likely installed NGINX via some kind of package manager (apt, yum, whatever) you’re still going to need the source for the version you’ve installed. Even worse, you’re going to need all the dependencies that the source was originally compiled against. From the horse’s mouth:
Dynamic modules must be compiled against the same version of NGINX they are loaded into.
And from the Google Brotli module installation docs:
You will need to use exactly the same ./configure arguments as your Nginx configuration and append –with-compat –add-dynamic-module=/path/to/ngx_brotli to the end, otherwise you will get a “module is not binary compatible” error on startup. You can run nginx -V to get the configuration arguments for your Nginx installation.
So that all sounds complicated and terrible, but fortunately it’s not too bad if you are OK with some blind faith and a little copy/pasting. In all the following examples, we’re using Ubuntu 20.04.
apt-get install build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev \ libssl-dev libgd-dev libxml2 libxml2-dev uuid-dev libxslt-dev
Once you’ve got those, you’re going to need the NGINX source and the Brotli module source.
nginx -v # nginx version: nginx/1.18.0 (Ubuntu)
Ok, so we need 1.18.0 since that’s what’s installed on our system. We also need to clone the Brotli module from Github. There are Git submodules in play, so make sure when you clone the Brotli module repository you use the
wget http://nginx.org/download/nginx-1.18.0.tar.gz tar -xzvf nginx-1.18.0.tar.gz # Ensure you use the --recursive flag git clone https://github.com/google/ngx_brotli.git --recursive
So remember all that talk about binary compatability? When NGINX was originally compiled for your system, it was compiled with a whole slew of configure arguments. When we compile the module, we need to make sure they are exactly the same, plus we need to add a few more! To see the original configure arguments we can use the
-V flag (note the capital letter this time)
Copy that huge list of configure arguments and paste it somewhere for safekeeping. Now comes the fun part.
cd nginx-1.18.0 # Paste in the configure arguments and then add the second line # If your system is different, ./configure may spit out # additional dependencies you need to install ./configure [ALL THE CONFIGURE ARGUMENTS] \ --with-compat --add-dynamic-module=../ngx_brotli # If ./configure finishes successfully, you can make the module! make modules
The compiled modules will be emitted to the
obj/ folder within the source directory once compilation is successful. We need to move the
.so files to a place NGINX can find them.
cp objs/ngx_http_brotli_filter_module.so /usr/lib/nginx/modules cp objs/ngx_http_brotli_static_module.so /usr/lib/nginx/modules
Huzzah! Now we’re ready to configure NGINX. Having fun yet?
This is the easy part. In
nginx.conf you need to load the module. This needs to be the very first thing in the file.
# for compressing responses on-the-fly load_module modules/ngx_http_brotli_filter_module.so; # for serving pre-compressed files load_module modules/ngx_http_brotli_static_module.so;
And then we need to turn it on! You can enable Brotli in specific location blocks, or at the server level.
And finally, we need to test our configuration to make sure we did everything right, and then reload NGINX
nginx -t && nginx -s reload
And if we Gzip it like we usually do, it’s 209KB , a 65% reduction.
But what happens when we enable Brotli instead?
The file is compressed to 161KB , a 48KB (23%) improvement over Gzip!
There’s only one way to find out. Use a tool like Request Metrics to capture real-world performance metrics from your users. Switch to using Brotli and measure the results! With our new custom tagging you could even run an A/B test with some machines compressing with Gzip and others using Brotli. It takes just seconds to set up the agent and start getting real data.