The Ops Community

Cover image for Using Brotli Compression in NGINX
Todd H. Gardner for Request Metrics

Posted on • Originally published at

Using Brotli Compression in NGINX

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?

Using Brotli in NGINX

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.

The Easy (but Expensive) Way

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.

The Hard Way

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

Installing Dependencies

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.

Installing all the Pre-Reqs

apt-get install build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev \ 
libssl-dev libgd-dev libxml2 libxml2-dev uuid-dev libxslt-dev

Enter fullscreen mode Exit fullscreen mode

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)

Enter fullscreen mode Exit fullscreen mode

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 --recursive flag!

tar -xzvf nginx-1.18.0.tar.gz

# Ensure you use the --recursive flag
git clone --recursive

Enter fullscreen mode Exit fullscreen mode

Getting the Configuration Arguments Right

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)

All the configure arguments

Copy that huge list of configure arguments and paste it somewhere for safekeeping. Now comes the fun part.

Compile Time, Baby

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
--with-compat --add-dynamic-module=../ngx_brotli

# If ./configure finishes successfully, you can make the module!
make modules

Enter fullscreen mode Exit fullscreen mode

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/ /usr/lib/nginx/modules
cp objs/ /usr/lib/nginx/modules

Enter fullscreen mode Exit fullscreen mode

Huzzah! Now we’re ready to configure NGINX. Having fun yet?

Configuring NGINX

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/;
# for serving pre-compressed files
load_module modules/; 

Enter fullscreen mode Exit fullscreen mode

And then we need to turn it on! You can enable Brotli in specific location blocks, or at the server level.

brotli on;
    # add more types as needed

Enter fullscreen mode Exit fullscreen mode

And finally, we need to test our configuration to make sure we did everything right, and then reload NGINX

nginx -t && nginx -s reload

Enter fullscreen mode Exit fullscreen mode

Brotli in Action

Let’s see how well it works! The JavaScript application bundle for Request Metrics is 600KB uncompressed:

No compression enabled

And if we Gzip it like we usually do, it’s 209KB , a 65% reduction.

Using Gzip for a baseline

But what happens when we enable Brotli instead?

Using Brotli instead of Gzip

The file is compressed to 161KB , a 48KB (23%) improvement over Gzip!

Is it Worth It?

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.

Discussion (0)