NGINX Brotli Compression: Install, Configure and Pre-Compress Static Assets

Gzip has been compressing web content since 1992. It’s good. It’s everywhere. And it’s showing its age. Brotli is its modern replacement — developed by Google, standardised in 2016 (RFC 7932), and now supported by every browser that matters. On typical web content, Brotli achieves 15–26% better compression than gzip at comparable speeds. Smaller files mean faster page loads, lower bandwidth costs, and better Core Web Vitals scores.

The myguard APT repository ships a native Brotli dynamic module for both NGINX and Angie — install it with one apt command, load it with one config line, and your server starts serving Brotli to every browser that supports it.

Brotli vs Gzip: The Actual Numbers

Brotli uses a combination of LZ77, Huffman coding, and a 2nd-order context modeling that gzip doesn’t have. The practical result:

Content typeGzip (level 6)Brotli (level 6)Brotli advantage
HTML68% reduction78% reduction+15%
CSS72% reduction84% reduction+21%
JavaScript67% reduction80% reduction+19%
JSON API response71% reduction83% reduction+17%
SVG74% reduction86% reduction+19%

Brotli level 11 (maximum) achieves 20–26% better compression than gzip, but is extremely slow to encode — suitable only for pre-compressed static assets, not on-the-fly. Level 4–6 is the sweet spot for on-the-fly dynamic compression: better than gzip, fast enough for real-time use.

Step 1 — Install the Brotli Module

# Add the myguard repository if not already done
wget https://deb.myguard.nl/pool/myguard.deb
dpkg -i myguard.deb
apt-get update

# Install NGINX with the Brotli module
apt-get install nginx libnginx-mod-http-brotli

# Or for Angie:
apt-get install angie angie-module-http-brotli

New to the myguard repository? Follow the two-minute setup guide.

Step 2 — Load the Module

The myguard package installs a load snippet automatically. Verify it’s in place:

ls /etc/nginx/modules-enabled/ | grep brotli
# Should show: 50-mod-http-brotli-filter.conf and 50-mod-http-brotli-static.conf

If not present, add to the top of nginx.conf (before the http block):

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

Step 3 — Configure Brotli

Add this inside your http block in nginx.conf:

http {
    # Brotli dynamic compression (on-the-fly)
    brotli             on;
    brotli_comp_level  6;        # 0-11, sweet spot is 4-6
    brotli_min_length  256;      # Don't compress tiny responses
    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
        image/svg+xml
        font/truetype
        font/opentype;

    # Brotli static files (serve pre-compressed .br files)
    brotli_static on;

    # Keep gzip as fallback for browsers that don't support Brotli
    gzip            on;
    gzip_comp_level 6;
    gzip_min_length 256;
    gzip_vary       on;
    gzip_types
        text/plain text/css text/javascript application/javascript
        application/json application/xml image/svg+xml font/opentype;
}

Step 4 — Test and Reload

nginx -t && systemctl reload nginx

Verify Brotli is working:

# curl with Brotli accept header
curl -H 'Accept-Encoding: br,gzip' -I https://example.com
# Look for: Content-Encoding: br

# Check Chrome DevTools: Network tab > select a request > Response Headers > Content-Encoding: br

Pre-Compressed Static Assets (Best Performance)

For static files that don’t change (CSS, JS, fonts), pre-compress them at build time with level 11 and let NGINX serve the .br files directly. This gives maximum compression with zero runtime CPU cost:

# Pre-compress all JS and CSS files in your web root
find /var/www/html -name '*.js' -o -name '*.css' | while read f; do
    brotli -Z -f "$f" -o "${f}.br"   # -Z = level 11
    gzip -9 -k -f "$f"               # -k = keep original, for fallback
done

With brotli_static on in your NGINX config, when a browser requests app.js with Accept-Encoding: br, NGINX automatically serves app.js.br without doing any runtime compression. Zero CPU, maximum compression.

# Install the brotli CLI tool
apt-get install brotli

Brotli for WordPress

WordPress sites benefit significantly from Brotli because WordPress generates a lot of HTML, CSS, and JavaScript. The main caveat: PHP responses are compressed dynamically, so set a sane compression level (4–6) to avoid adding more than ~1ms of CPU time per request.

Typical page size reduction for a WordPress homepage:

  • Uncompressed HTML: ~180KB
  • Gzip level 6: ~28KB
  • Brotli level 6: ~23KB
  • Brotli level 11 (pre-compressed): ~19KB

The 5KB difference between gzip and Brotli level 6 saves ~40ms on a typical 4G connection. Across thousands of page views, that’s meaningful for Core Web Vitals.

Brotli + zstd: Running Both

The myguard repository also ships a zstd NGINX module. zstd excels at server-side API compression (faster decode, great for JSON) while Brotli excels at browser-facing content (better compression ratio). Run both:

apt-get install libnginx-mod-http-brotli libnginx-mod-http-zstd

# In http block:
brotli on;        # For browsers (HTML, CSS, JS)
brotli_types text/html text/css application/javascript image/svg+xml;

zstd on;          # For API clients that support it
zstd_types application/json application/x-ndjson;
zstd_comp_level 3;

Frequently Asked Questions

Do all browsers support Brotli?
Every browser released after 2017 supports Brotli — Chrome, Firefox, Safari, Edge. Coverage is 96%+ of global browser usage. NGINX with Brotli still serves gzip to the ~4% that don’t support it (IE11, very old Safari), so there’s no compatibility risk.
Does Brotli work with HTTPS only?
Technically no — Brotli can work over HTTP. But all browsers only send the Accept-Encoding: br header on HTTPS connections, because early Brotli deployments over HTTP caused issues with some HTTP proxies. In practice: Brotli only activates on HTTPS, which is fine since you should be using HTTPS anyway.
What compression level should I use?
Level 4–6 for dynamic on-the-fly compression (good ratio, fast). Level 11 only for pre-compressed static assets (maximum ratio, but too slow for real-time use). The default of 6 in the config above is the practical sweet spot for most sites.
Does Brotli affect CPU usage?
At level 6, dynamic Brotli adds roughly 1–2ms of CPU time per response compared to gzip. On a server handling 500 req/s that’s about 3–5% extra CPU load. Pre-compressed static assets with brotli_static eliminate runtime CPU entirely for those files.
Can I use Brotli with Angie?
Yes. Install angie-module-http-brotli instead of libnginx-mod-http-brotli. The configuration directives are identical.
Should I disable gzip when using Brotli?
No — keep gzip enabled alongside Brotli. NGINX automatically serves Brotli to browsers that support it and gzip to those that don’t. Disabling gzip would break compression for the small percentage of users on older browsers or corporate proxies that strip Brotli support.

Related Posts