Brotli compression offers 15-25% better compression ratios than gzip for text-based assets like HTML, CSS, and JavaScript. While Nginx doesn't include Brotli by default, you can add it as a dynamic module. This guide covers building and configuring Nginx with Brotli support.
Build Nginx with Brotli Module
# Install dependencies
sudo apt install -y build-essential git libpcre3-dev zlib1g-dev libssl-dev
# Get current Nginx version
NGINX_VER=$(nginx -v 2>&1 | grep -oP '\d+\.\d+\.\d+')
echo "Current Nginx: $NGINX_VER"
# Download Nginx source
cd /opt
wget https://nginx.org/download/nginx-${NGINX_VER}.tar.gz
tar xzf nginx-${NGINX_VER}.tar.gz
# Clone Brotli module
git clone --recurse-submodules https://github.com/google/ngx_brotli.git
# Build Brotli dependencies
cd ngx_brotli/deps/brotli
mkdir out && cd out
cmake -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF ..
cmake --build . --config Release
# Build Nginx with Brotli as dynamic module
cd /opt/nginx-${NGINX_VER}
./configure --with-compat --add-dynamic-module=/opt/ngx_brotli
make modules
# Install modules
sudo cp objs/ngx_http_brotli_filter_module.so /etc/nginx/modules/
sudo cp objs/ngx_http_brotli_static_module.so /etc/nginx/modules/
Configure Brotli
# /etc/nginx/nginx.conf — load modules at top
load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;
http {
# Brotli on-the-fly compression
brotli on;
brotli_comp_level 6; # 0-11, higher = better compression, slower
brotli_static on; # Serve pre-compressed .br files if available
brotli_min_length 256; # Don't compress tiny files
brotli_types
text/plain
text/css
text/javascript
text/xml
text/x-component
application/javascript
application/json
application/xml
application/rss+xml
application/atom+xml
application/vnd.ms-fontobject
application/x-font-ttf
application/x-font-opentype
application/x-web-app-manifest+json
font/opentype
image/svg+xml
image/x-icon;
# Keep gzip as fallback for clients that don't support Brotli
gzip on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_types text/plain text/css application/javascript application/json
application/xml text/xml image/svg+xml;
}
# Test and reload
sudo nginx -t
sudo systemctl reload nginx
Pre-Compress Static Assets
# Pre-compress files for brotli_static
# This avoids CPU overhead of on-the-fly compression
# Install brotli CLI
sudo apt install brotli
# Compress all static assets
find /var/www/html -type f \( -name "*.css" -o -name "*.js" -o -name "*.html" -o -name "*.svg" -o -name "*.json" -o -name "*.xml" \) -exec brotli -f -k {} \;
# Build script for your deployment pipeline
#!/bin/bash
# pre-compress.sh
WEBROOT="/var/www/html"
find "$WEBROOT" -type f \( \
-name "*.css" -o -name "*.js" -o -name "*.html" \
-o -name "*.svg" -o -name "*.json" -o -name "*.xml" \
-o -name "*.txt" -o -name "*.woff2" \
\) | while read file; do
brotli -f -k --quality=11 "$file" # Max compression for static
gzip -f -k -9 "$file" # Also create .gz for fallback
done
echo "Pre-compressed $(find $WEBROOT -name '*.br' | wc -l) files"
Verify Brotli is Working
# Test with curl
curl -sI -H 'Accept-Encoding: br' https://example.com | grep -i content-encoding
# Should show: content-encoding: br
# Compare compression ratios
# Original size
curl -so /dev/null -w '%{size_download}' https://example.com/style.css
# Gzip compressed
curl -so /dev/null -w '%{size_download}' -H 'Accept-Encoding: gzip' https://example.com/style.css
# Brotli compressed
curl -so /dev/null -w '%{size_download}' -H 'Accept-Encoding: br' https://example.com/style.css
Performance Tuning
# Compression level trade-offs:
# Level 1-4: Fast, ~10% better than gzip
# Level 5-6: Good balance (recommended for dynamic content)
# Level 7-9: Slower, diminishing returns
# Level 10-11: Very slow, best for pre-compression only
# For dynamic content (on-the-fly):
brotli_comp_level 4; # Fast enough for real-time
# For pre-compressed static files:
brotli --quality=11 # Maximum compression, done once
Best Practices
- Use level 4-6 for dynamic content — higher levels are too slow for real-time compression
- Pre-compress static assets at level 11 — maximum compression, done once during build
- Enable
brotli_staticto serve pre-compressed .br files automatically - Keep gzip as fallback: Some clients and proxies don't support Brotli
- Only compress text-based content: Images and videos are already compressed
- Set
brotli_min_lengthto avoid compressing tiny responses (overhead > savings)