How to: Brotli compression with Nginx

A how to guide for compiling Nginx from source to add the Brotli compression module and boost website performance with improved compression.

If you're new to Brotli compression, I'd recommend reading our earlier blog post on Brotli compression, that provides an introduction and background information.

Back to topInstall the prerequisites

Before we can compile Nginx we need to install the prerequisites:

apt-get install build-essential zlib1g-dev libpcre3-dev libssl-dev libxslt1-dev libxml2-dev libgd2-xpm-dev libgeoip-dev libgoogle-perftools-dev libperl-dev

Back to topDownload the sources

The directory you use for building from source is up to you but /usr/local/src is not a bad choice, the rest of this guide assumes your sources are in this location.

Download the source for the latest stable version of Nginx from the downloads page, at the time of writing that is 1.10.3, update the version number in the commands below to the latest version available:

cd /usr/local/src
wget http://nginx.org/download/nginx-1.10.3.tar.gz
tar -xzvf nginx-1.10.3.tar.gz

Download the source for the Brotli compression Nginx module from GitHub:

cd /usr/local/src
git clone https://github.com/google/ngx_brotli.git
cd ngx_brotli
git submodule update --init --recursive

Back to topGet your existing Nginx configuration arguments

When building Nginx from source, there are a lot of different elements you can configure such as modules, features, paths, etc.  In order to match our existing configuration with the addition of the Brotli module we need to grab the current configuration arguments and append the argument to include the Brotli module (please note: uppercase "V"):

nginx -V

The output should be similar to the following:

nginx version: nginx/1.10.3
built with OpenSSL 1.0.1f 6 Jan 2014
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2 -fPIE -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -fPIC -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -fPIE -pie -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-debug --with-pcre-jit --with-ipv6 --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 --with-http_addition_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_xslt_module=dynamic --with-stream=dynamic --with-stream_ssl_module --with-mail=dynamic --with-mail_ssl_module --add-dynamic-module=/build/nginx-wa6Kgn/nginx-1.10.3/debian/modules/nginx-auth-pam --add-dynamic-module=/build/nginx-wa6Kgn/nginx-1.10.3/debian/modules/nginx-dav-ext-module --add-dynamic-module=/build/nginx-wa6Kgn/nginx-1.10.3/debian/modules/nginx-echo --add-dynamic-module=/build/nginx-wa6Kgn/nginx-1.10.3/debian/modules/nginx-upstream-fair --add-dynamic-module=/build/nginx-wa6Kgn/nginx-1.10.3/debian/modules/ngx_http_substitutions_filter_module

Copy everything after "configuration arguments:":

--with-cc-opt='-g -O2 -fPIE -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -fPIC -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -fPIE -pie -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-debug --with-pcre-jit --with-ipv6 --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 --with-http_addition_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_xslt_module=dynamic --with-stream=dynamic --with-stream_ssl_module --with-mail=dynamic --with-mail_ssl_module --add-dynamic-module=/build/nginx-wa6Kgn/nginx-1.10.3/debian/modules/nginx-auth-pam --add-dynamic-module=/build/nginx-wa6Kgn/nginx-1.10.3/debian/modules/nginx-dav-ext-module --add-dynamic-module=/build/nginx-wa6Kgn/nginx-1.10.3/debian/modules/nginx-echo --add-dynamic-module=/build/nginx-wa6Kgn/nginx-1.10.3/debian/modules/nginx-upstream-fair --add-dynamic-module=/build/nginx-wa6Kgn/nginx-1.10.3/debian/modules/ngx_http_substitutions_filter_module

We need to add some additional configuration arguments to the end:

--add-module=/usr/local/src/ngx_brotli --sbin-path=/usr/sbin/nginx

We'll pass these configuration arguments to our ./configure call, with one further change to remove any "--add-dynamic-module" arguments so the full command, in my case, will be as follows:

./configure --with-cc-opt='-g -O2 -fPIE -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -fPIC -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -fPIE -pie -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-debug --with-pcre-jit --with-ipv6 --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 --with-http_addition_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_xslt_module=dynamic --with-stream=dynamic --with-stream_ssl_module --with-mail=dynamic --with-mail_ssl_module --add-module=/usr/local/src/ngx_brotli

The following dynamic modules were removed:

--add-dynamic-module=/build/nginx-wa6Kgn/nginx-1.10.3/debian/modules/nginx-auth-pam --add-dynamic-module=/build/nginx-wa6Kgn/nginx-1.10.3/debian/modules/nginx-dav-ext-module --add-dynamic-module=/build/nginx-wa6Kgn/nginx-1.10.3/debian/modules/nginx-echo --add-dynamic-module=/build/nginx-wa6Kgn/nginx-1.10.3/debian/modules/nginx-upstream-fair --add-dynamic-module=/build/nginx-wa6Kgn/nginx-1.10.3/debian/modules/ngx_http_substitutions_filter_module

Then we can compile Nginx with the "./configure …" command from earlier.

cd /usr/local/src/nginx-1.10.3
./configure --with-cc-opt='-g -O2 -fPIE -fstack-protector --param=ssp-buffer-size=4 -Wformat -Werror=format-security -fPIC -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-Bsymbolic-functions -fPIE -pie -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-debug --with-pcre-jit --with-ipv6 --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 --with-http_addition_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_xslt_module=dynamic --with-stream=dynamic --with-stream_ssl_module --with-mail=dynamic --with-mail_ssl_module --add-module=/usr/local/src/ngx_brotli

Back to topInstall the compiled Nginx package

Install Nginx as follows, this also removes any existing versions of Nginx, which can cause errors otherwise:

make
apt-get remove nginx
checkinstall

Next we need to restart Nginx and check we have the latest version running (please note: lowercase "v"):

service nginx restart
nginx -v

You should see the following output:

nginx version: nginx/1.10.3

Back to topEnable Brotli compression in Nginx

We now have Nginx installed with the Brotli compression module so next we need to adjust the Nginx configuration (nginx.conf) to enable it:

http {
  …
  brotli on;
  brotli_static on;
  brotli_types *;
  …
}

Run "nginx -t" to ensure there are no errors in the new configuration then reload Nginx "service nginx reload" so the changes take effect.

Back to topNotes

The above configuration enables the two flavours of Brotli compression, those being "brotli", which is on-the-fly compression for requests Nginx serves and "brotli_static", which is where Nginx can serve pre-compressed files appended with a .br extension e.g. styles.css.br. Due to the slow nature of performing Brotli compression, using pre-compressed files wherever possible is recommended or caching the output from Nginx.  It's also possible to specify MIME-types instead of the wildcard approach above.

If you're using Nginx behind a caching HTTP reverse proxy such as Varnish or a CDN you might want to check any normalising that the Accept-Encoding header is subject to. Often such services will normalise the Accept-Encoding header to just "gzip" even if Brotli is supported.

Tagged with:

You might also like...

Vim: Demystifying the Beast

Seb Jones by Seb Jones