# Nginx Brotli Compression Not Working
Brotli compression should make responses smaller than gzip, but responses aren't compressed at all, or they're compressed with gzip instead. The Content-Encoding: br header doesn't appear, even though Brotli is configured. Brotli offers better compression ratios than gzip, but the module and configuration must be correct.
Introduction
This article covers troubleshooting steps and solutions for How to Fix Nginx Brotli Compression Not Working. The error typically occurs in production environments and can cause service disruptions if not addressed promptly.
Symptoms
Common error messages include:
curl -H "Accept-Encoding: br, gzip" -I http://example.com | grep -i "content-encoding"```bash # Check for module in compile options nginx -V 2>&1 | grep brotli
# Check for dynamic module ls /etc/nginx/modules/ | grep brotli ls /usr/lib/nginx/modules/ | grep brotli
# Check if module is loaded in config grep -r "load_module" /etc/nginx/ | grep brotli ```
```bash # Ubuntu/Debian (from nginx-extras or custom repo) sudo apt install libnginx-mod-http-brotli
# Or add module repository sudo add-apt-repository ppa:ondrej/nginx sudo apt update sudo apt install nginx-module-brotli
# CentOS/RHEL sudo yum install nginx-module-brotli ```
Common Causes
- Configuration misconfiguration
- Missing or incorrect credentials
- Network connectivity issues
- Version compatibility problems
- Resource exhaustion or limits
- Permission or access denied
Step-by-Step Fix
- 1.Check logs for specific error messages
- 2.Verify configuration settings
- 3.Test network connectivity
- 4.Review recent changes
- 5.Apply corrective action
- 6.Verify the fix
Understanding Brotli in Nginx
- 1.Brotli requires:
- 2.Brotli module (
ngx_http_brotli_module) - 3.Client support (Accept-Encoding: br)
- 4.Correct MIME types configured
- 5.Sufficient quality level
Check compression status:
``bash
curl -H "Accept-Encoding: br, gzip" -I http://example.com | grep -i "content-encoding"
Expect: Content-Encoding: br or Content-Encoding: gzip
Common Cause 1: Brotli Module Not Installed
The module isn't compiled into Nginx or loaded as dynamic module.
Diagnosis: ```bash # Check for module in compile options nginx -V 2>&1 | grep brotli
# Check for dynamic module ls /etc/nginx/modules/ | grep brotli ls /usr/lib/nginx/modules/ | grep brotli
# Check if module is loaded in config grep -r "load_module" /etc/nginx/ | grep brotli ```
Solution: Install Brotli module: ```bash # Ubuntu/Debian (from nginx-extras or custom repo) sudo apt install libnginx-mod-http-brotli
# Or add module repository sudo add-apt-repository ppa:ondrej/nginx sudo apt update sudo apt install nginx-module-brotli
# CentOS/RHEL sudo yum install nginx-module-brotli ```
Load dynamic module:
``nginx
# In nginx.conf main context (before http block)
load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;
Verify module loaded:
``bash
sudo nginx -t
# Should not error about unknown directive "brotli"
Common Cause 2: Brotli Not Enabled in Configuration
Module loaded but compression not enabled.
Problematic config:
``nginx
http {
# Module loaded but no brotli directives
gzip on;
}
Solution: Enable Brotli: ```nginx http { brotli on; brotli_comp_level 6; brotli_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/svg+xml;
# Minimum length to compress (default 256) brotli_min_length 256;
# Also keep gzip for older clients gzip on; gzip_comp_level 6; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript; } ```
Common Cause 3: MIME Types Not Configured
Only certain MIME types are compressed by default.
Default Brotli types: Only text/html
Problem:
``nginx
brotli on;
# Only HTML compressed, not CSS/JS/JSON
Solution: Add all compressible types:
``nginx
brotli on;
brotli_types text/plain text/css text/xml application/json application/javascript application/xml application/xml+rss application/atom+xml image/svg+xml text/javascript text/x-component text/x-cross-domain-policy;
Check current response type: ```bash curl -I http://example.com/style.css | grep Content-Type # Content-Type: text/css
# If response shows Content-Type, verify it's in brotli_types ```
Common Cause 4: Client Doesn't Support Brotli
Brotli only used if client requests it.
Diagnosis: ```bash # Request with Brotli support curl -H "Accept-Encoding: br, gzip, deflate" -I http://example.com
# Request without Brotli support curl -H "Accept-Encoding: gzip, deflate" -I http://example.com ```
Browser support: - Chrome 50+, Firefox 44+, Edge 15+, Safari 11+ - Older browsers fall back to gzip
Test with different Accept-Encoding: ```bash # Brotli only curl -H "Accept-Encoding: br" -I http://example.com/style.css
# Should see: Content-Encoding: br ```
Common Cause 5: Compression Level Too Low
Quality level affects compression ratio and CPU usage.
Problem: Level 1 gives minimal compression:
``nginx
brotli on;
brotli_comp_level 1; # Minimal compression
Solution: Use balanced level: ```nginx brotli on; brotli_comp_level 6; # Good balance (default is 4)
# Level 4: Fast, moderate compression # Level 6: Good compression, reasonable CPU # Level 11: Maximum compression, high CPU ```
Compare compression ratios: ```bash # Test uncompressed curl -s http://example.com/style.css > uncompressed.css wc -c uncompressed.css
# Test brotli level 4 curl -s -H "Accept-Encoding: br" http://example.com/style.css > compressed.br wc -c compressed.br
# Test brotli level 11 (if configured) curl -s -H "Accept-Encoding: br" http://example.com/style.css > compressed-11.br wc -c compressed-11.br ```
Common Cause 6: Response Too Small
Small responses aren't compressed (below brotli_min_length).
Default: 256 bytes
Problem:
``nginx
brotli on;
brotli_min_length 256;
# 100-byte responses not compressed
Solution: Lower minimum if needed:
``nginx
brotli on;
brotli_min_length 100;
Or check response size:
``bash
curl -s http://example.com/small.json | wc -c
# If < 256, won't be compressed
Common Cause 7: Pre-compressed Static Files Conflict
Static module conflicts with dynamic compression.
Brotli static module: Serves pre-compressed .br files
Brotli filter module: Compresses on-the-fly
Using static pre-compression:
``nginx
brotli_static on;
# Looks for file.css.br and serves it if exists
Problem: .br file exists but wrong content.
Solution: Verify pre-compressed files: ```bash # Check if .br file exists ls -la /var/www/html/style.css.br
# Test pre-compressed file curl -s -H "Accept-Encoding: br" -I http://example.com/style.css
# Should see Content-Encoding: br and correct size ```
Generate pre-compressed files: ```bash # Compress all static assets find /var/www/html -name '*.css' -exec brotli {} \; find /var/www/html -name '*.js' -exec brotli {} \;
# Or parallel find /var/www/html -name '*.css' -print0 | xargs -0 -P4 brotli ```
Common Cause 8: Vary Header Issues
Vary: Accept-Encoding must be set for proper caching.
Problem:
``nginx
brotli on;
# Missing Vary header, cache might serve compressed to non-brotli client
Solution: Ensure Vary header: ```nginx brotli on; brotli_types text/plain text/css application/json;
# Nginx adds Vary automatically, but verify add_header Vary Accept-Encoding always; ```
Check Vary header:
``bash
curl -I http://example.com/style.css | grep Vary
# Vary: Accept-Encoding
Common Cause 9: Double Compression
Response already compressed upstream.
Problem:
``nginx
location /api {
brotli on;
proxy_pass http://backend;
# Backend returns gzip-compressed response
# Nginx tries to brotli compress gzip -> garbage
}
Solution: Decompress upstream or disable compression: ```nginx # Option 1: Tell upstream not to compress location /api { brotli on; proxy_pass http://backend; proxy_set_header Accept-Encoding ""; # Backend returns uncompressed }
# Option 2: Don't compress proxied responses location /api { proxy_pass http://backend; # Let backend handle compression } ```
Verification
- 1.Check module loaded:
- 2.```bash
- 3.sudo nginx -t 2>&1 | grep -i brotli
- 4.
` - 5.Check Brotli is on:
- 6.```nginx
- 7.location /brotli-test {
- 8.brotli on;
- 9.return 200 "test content here with enough bytes for compression minimum length requirement satisfied";
- 10.}
- 11.
`
curl -H "Accept-Encoding: br" -I http://localhost/brotli-test
# Should show: Content-Encoding: br- 1.Compare sizes:
- 2.```bash
- 3.# Uncompressed
- 4.curl -s http://example.com/style.css | wc -c
# Brotli compressed curl -s -H "Accept-Encoding: br" http://example.com/style.css | wc -c ```
- 1.Check MIME types:
- 2.```bash
- 3.sudo nginx -T | grep brotli_types
- 4.
`
Complete Working Configuration
```nginx load_module modules/ngx_http_brotli_filter_module.so; load_module modules/ngx_http_brotli_static_module.so;
http { brotli on; brotli_comp_level 6; brotli_min_length 256; brotli_buffers 16 8k;
brotli_types text/plain text/css text/xml application/json application/javascript application/xml application/xml+rss application/atom+xml image/svg+xml text/javascript application/x-javascript text/x-component text/x-cross-domain-policy application/font-ttf application/font-otf;
gzip on; gzip_comp_level 6; gzip_min_length 256; gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
gzip_vary on;
server { listen 80; server_name example.com;
root /var/www/html;
# Static files - use pre-compressed if available location ~* \.(css|js)$ { brotli_static on; expires 1y; add_header Cache-Control "public, immutable"; add_header Vary Accept-Encoding; }
# Dynamic content - compress on-the-fly location / { brotli on; try_files $uri $uri/ =404; } } } ```
Verification
| Symptom | Cause | Fix |
|---|---|---|
| No br header | Module not loaded | Install/load module |
| No br header | brotli not on | Add brotli on; |
| CSS/JS not compressed | Missing MIME types | Add to brotli_types |
| Small files not compressed | Below min_length | Lower brotli_min_length |
| Old clients get garbage | No gzip fallback | Keep gzip enabled |
| Cached wrong version | Missing Vary header | Add Vary: Accept-Encoding |
| Pre-compressed not served | Static module missing | Load static module |
Brotli compression requires the module, correct MIME types, and proper client negotiation. Verify module loading first, then check MIME type configuration and compression settings.
Related Articles
- [Nginx troubleshooting: Fix Lambda Permission Denied - Complete ](fix-lambda-permission-denied)
- [Nginx web server troubleshooting: Fix Client Max Body Size Large Upload Nginx Issue ](client-max-body-size-large-upload-nginx)
- [Fix Apache 502 Proxy Error](fix-apache-502-proxy-error)
- [Fix Apache LogLevel Core Debug Configuration](fix-apache-loglevel-core-debug)
- [Fix Cloudflare 502 Bad Gateway Error](fix-cloudflare-502-bad-gateway)
<script type="application/ld+json"> { "@context": "https://schema.org", "@type": "TechArticle", "headline": "How to Fix Nginx Brotli Compression Not Working", "description": "Troubleshoot Nginx Brotli compression failures. Fix module loading, MIME type configuration, and compression quality settings.", "url": "https://www.fixwikihub.com/fix-nginx-brotli-not-working", "publisher": { "@type": "Organization", "name": "FixWikiHub", "url": "https://www.fixwikihub.com" }, "author": { "@type": "Person", "name": "FixWikiHub Editorial Team" }, "datePublished": "2025-11-28T03:19:19.651Z", "dateModified": "2025-11-28T03:19:19.651Z" } </script>