This article may contain affiliate links. If you buy some products using those links, I may receive monetary benefits. See affiliate disclosure here

Brotli is a highly efficient compression algorithm that can greatly reduce the size of your website's HTML, CSS, JavaScript, etc.

In another post, I'd written a detailed comparison showing Brotli performs against gzip, another popular compression algorithm used on the web. Brotli outperformed gzip almost always.

However, Brotli is not supported out of the box on Nginx. So you need to enable it yourself. There are a few ways to do that. And in this post, I'll show you how I did it by following the instructions given on this page.

Steps to enable Brotli in Nginx

In my case, I already had Nginx web server running. So I needed to enable the Brotli module with the existing Nginx installation rather than performing a fresh installation.

1. Download the necessary dependencies

My server was running Ubuntu 22.04 LTS. And I had to install the following tools.

sudo apt update
sudo apt install \ 
    build-essential \
    zlib1g-dev \
    libpcre3 \
    libpcre3-dev \
    unzip \
    git \
    cmake

2. Download the appropriate Nginx version

Upon checking the existing version, I could identify that I was running Nginx v1.18.0, a few versions behind the currently available stable version, as of writing this post.

nginx -v

The next step is downloading that particular version from the Nginx website. I downloaded it to my home folder. Then extracted the archive.

cd /home/abhinav/
wget https://nginx.org/download/nginx-1.18.0.tar.gz  
tar -xzf nginx-1.18.0.tar.gz

3. Cloning the ngx_brotli module

Next, clone the ngx_brotli module from Google's GitHub repository.

git clone --recurse-submodules -j8 https://github.com/google/ngx_brotli

4. Build ngx_brotli module

Once it is cloned, we need to build the module.

cd /home/abhinav/ngx_brotli/deps/brotli 

mkdir out && cd out  

cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF -DCMAKE_C_FLAGS="-Ofast -m64 -march=native -mtune=native -flto -funroll-loops -ffunction-sections -fdata-sections -Wl,--gc-sections" -DCMAKE_CXX_FLAGS="-Ofast -m64 -march=native -mtune=native -flto -funroll-loops -ffunction-sections -fdata-sections -Wl,--gc-sections" -DCMAKE_INSTALL_PREFIX=./installed ..  

cmake --build . --config Release --target brotlienc

cd /home/abhinav/ 

5. Backup the existing Nginx configuration

It is always a good idea to take a backup of your Nginx configuration, in case something goes wrong.

sudo cp -r /etc/nginx /etc/nginx.backup  

6. Find the current Nginx build configuration

Now, we need to find out the arguments with which our existing Nginx instance was built. For that you can run the nginx -V command. This will help us to correctly rebuild Nginx later.

nginx -V

Upon running the command, I could see that Nginx was compiled with a couple of modules as you can see in this screenshot:

nginx build configuration check

In order to rebuild it with the same configuration later, I had to install one more package - libssl-dev, which was currently missing on my system. When I tried without this package, the configure (next step) was exiting with an error.

sudo apt install libssl-dev

7. Configure the Build

Now we're ready to configure the new build. So first, move to the downloaded Nginx folder.

Then add all the previous configure arguments, plus the new module - --add-dynamic-module=/home/abhinav/ngx_brotli

cd /home/abhinav/nginx-1.18.0/

./configure \
    --with-cc-opt='-g -O2 -ffile-prefix-map=/build/nginx-dSlJVq/nginx-1.18.0=. -flto=auto -ffat-lto-objects -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' \
    --with-ld-opt='-Wl,-Bsymbolic-functions -flto=auto -ffat-lto-objects -Wl,-z,relro -Wl,-z,now -fPIC' \
    --prefix=/usr/share/nginx \
    --conf-path=/etc/nginx/nginx.conf \
    --http-log-path=/var/log/nginx/access.log \
    --error-log-path=/var/log/nginx/error.log \
    --lock-path=/var/lock/nginx.lock \
    --pid-path=/run/nginx.pid \
    --modules-path=/usr/lib/nginx/modules \
    --http-client-body-temp-path=/var/lib/nginx/body \
    --http-fastcgi-temp-path=/var/lib/nginx/fastcgi \
    --http-proxy-temp-path=/var/lib/nginx/proxy \
    --http-scgi-temp-path=/var/lib/nginx/scgi \
    --http-uwsgi-temp-path=/var/lib/nginx/uwsgi \
    --with-compat \
    --with-debug \
    --with-pcre-jit \
    --with-http_ssl_module \
    --with-http_stub_status_module \
    --with-http_realip_module \
    --with-http_auth_request_module \
    --with-http_v2_module \
    --with-http_dav_module \
    --with-http_slice_module \
    --with-threads \
    --add-dynamic-module=/home/abhinav/ngx_brotli

Note that we're adding it as a dynamic module. You could also make it static, but that would require us to build Nginx into a single binary with all the modules. Whereas dynamic keeps them separate, so updating and managing will be easier

8. Rebuild Nginx Modules

Run the make command to rebuild Nginx with the new configuration:

make modules

If everything goes well, the modules will be rebuilt as per the arguments passed to ./configure.

Note that we're only rebuilding the modules, not the entire Nginx. If you want to fully rebuild Nginx with the new configuration, you can run the following commands instead:

make
make install

The make install command will replace the current Nginx installation with the new one. But we don't need it. We only need to get the modules.

9. Copy the Shared Object (.so) Files to the appropriate location

Now if you check inside the objs folder, you will find some newly generated .so files. I had to move them to /usr/lib/nginx/modules.

ls objs  

sudo cp objs/ngx_http_brotli_filter_module.so /usr/lib/nginx/modules/  
sudo cp objs/ngx_http_brotli_static_module.so /usr/lib/nginx/modules/  

10. Update the Nginx Configuration File

Now that the Brotli module is in place, we need to link it from the Nginx configuration file. So open it in the nano or micro editor.

sudo micro /etc/nginx/nginx.conf

Now, outside the http block, load the modules:

load_module modules/ngx_http_brotli_filter_module.so;  
load_module modules/ngx_http_brotli_static_module.so;

Then inside the http block, add the compression settings:

http {      
   # Brotli Settings  
   brotli on;  
   brotli_static on;  
   brotli_comp_level 6;  
   brotli_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;

    # remaining settings...
}

11. Restart Nginx

Check the configuration and restart Nginx for the changes to take effect:

sudo nginx -t  
sudo systemctl restart nginx

12. Verify if it's Working

Just send a curl request to one of the sites running under our Nginx web server.

curl -H "Accept-Encoding: br" -I https://mysite.com

The returned headers should contain content-encoding: br, which verifies that Brotli compression is working as expected.

Caution when updating Nginx in the future

Here I've built the brotli module with Nginx v1.18.0. So if I update Nginx to a later version, I need to rebuild the brotli module again with that version. Otherwise, there will be a module version mismatch, and Nginx will stop working.

Once when I updated the system packages from the Ubuntu Packages manager, Nginx got updated to 1.26.2, and this happened:

brotli stopped working when nginx updated

So I had to re-download Nginx 1.26.2 and redo the above steps to make it working.

To avoid this situation, you can hold Nginx to a particular version, so that system updates won't update Nginx.

sudo apt-mark hold nginx

Now, when you intentionally want to upgrade Nginx, un-hold it, perform the upgrade, and hold again:

sudo apt-mark unhold nginx
sudo apt update
sudo apt upgrade nginx
sudo apt-mark hold nginx

To view the list of packages on hold, run the command:

apt-mark showhold

Conclusion

This is just one method to enable Brotli compression. I had also read somewhere that there is a PPA that makes it easier on Ubuntu. There is also a docker image for Nginx with Brotli enabled, but I haven't looked into it. So far, the above method is working fine for me.

Moreover, if you are using a CDN with full page caching, like Cloudflare, chances are that your content is already compressed with Brotli before being sent to visitors.

Another compression algorithm seen these days is zstd, and it said to be faster in compressing and decompressing. But as far as I know, zstd is not as widely supported by browsers as Brotli or gzip.