Using Brotli Compression in NGINX
Tags: webdev, performance, nginx, brotli
Published: 2022-05-23 18:13:21

---
title: Using Brotli Compression in NGINX
published: true
date: 2022-05-23 13:00:00 UTC
tags: webdev, performance, nginx, brotli
canonical_url: https://requestmetrics.com/web-performance/brotli-nginx
cover_image: https://requestmetrics.com/assets/images/webperf/brotli/brotli-teaser-2000.min.png
---

[Brotli](https://en.wikipedia.org/wiki/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](https://datatracker.ietf.org/doc/html/rfc7932). The sales pitch for Brotli is [better compression](https://paulcalvano.com/2018-07-25-brotli-compression-how-much-will-it-reduce-your-content/) 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](https://github.com/google/ngx_brotli) is currently developed and maintained by the folks at Google.

### The Easy (but Expensive) Way

NGINX Plus has [official support](https://docs.nginx.com/nginx/admin-guide/dynamic-modules/brotli/) 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](https://www.nginx.com/blog/creating-installable-packages-dynamic-modules/):

> Dynamic modules must be compiled against the same version of NGINX they are loaded into.

And from the Google Brotli [module installation](https://github.com/google/ngx_brotli#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

```bash

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.

```bash

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

```bash

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

```

### 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](https://requestmetrics.com/assets/images/webperf/brotli/configure-arguments.min.png)

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

### Compile Time, Baby

```bash

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.

```bash

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?

## 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.

```conf

# 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.

```conf

brotli on;
brotli_types
    application/javascript
    application/json
    image/svg+xml
    text/css
    text/plain;
    # add more types as needed

```

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

```bash
nginx -t && nginx -s reload

```

## Brotli in Action

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

![Uncompressed](https://requestmetrics.com/assets/images/webperf/brotli/js-uncompressed.min.png)
_No compression enabled_

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

![Gzipped](https://requestmetrics.com/assets/images/webperf/brotli/js-gzip.min.png)
_Using Gzip for a baseline_

But what happens when we enable **Brotli** instead?

![Brotli](https://requestmetrics.com/assets/images/webperf/brotli/js-brotli.min.png)
_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](/guide/tags) 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.