Skip to main content

Gzip & Brotli Compression: Reducing Transfer Size

Compressing text-based content (HTML, CSS, JS, JSON) reduces network transfer by 60–80% and dramatically improves page load times. Nginx supports both Gzip and Brotli compression.


Gzip vs Brotli

ItemGzipBrotli
StandardRFC 1952 (1996)RFC 7932 (2016)
Compression ratioBaseline~15–25% better
SpeedFastLevels 1–4 are similar; high levels are slower
Browser supportAll browsersModern browsers (not IE)
Requires HTTPSNoBrowsers only request it over HTTPS
Best useGeneral purposePre-compressing static files

Nginx Gzip Configuration

# /etc/nginx/nginx.conf — http block

http {
# Enable Gzip
gzip on;

# Minimum file size (smaller files are not worth compressing)
gzip_min_length 1024; # skip files under 1 KB

# Compression level: 1 (fast, low ratio) – 9 (slow, high ratio)
# 5–6 is the CPU-efficiency sweet spot
gzip_comp_level 5;

# MIME types to compress (images/video are already compressed — skip them)
gzip_types
text/plain
text/css
text/javascript
application/javascript
application/json
application/xml
application/rss+xml
image/svg+xml
font/ttf
font/woff
font/woff2;

# Compress responses forwarded through a proxy
gzip_proxied any;

# Add Vary: Accept-Encoding (helps cache distinguish compressed vs plain)
gzip_vary on;

# Disable for IE6 (buggy Gzip support)
gzip_disable "MSIE [1-6]\.";

# Compression buffer
gzip_buffers 32 4k;

# Apply compression even for HTTP/1.0 requests
gzip_http_version 1.0;
}

Nginx Brotli Configuration

Brotli requires a separate module. On Ubuntu install libnginx-mod-http-brotli-filter.

# Ubuntu
sudo apt install libnginx-mod-http-brotli-filter libnginx-mod-http-brotli-static

# Or compile from source
# --add-module=/path/to/ngx_brotli
# /etc/nginx/nginx.conf — http block

http {
# Dynamic Brotli compression
brotli on;
brotli_comp_level 4; # 0–11, levels 4–6 recommended (CPU efficient)
brotli_min_length 1024;
brotli_types
text/plain
text/css
text/javascript
application/javascript
application/json
application/xml
image/svg+xml;
}

Pre-compression — Best Performance

For static files, compress at deploy time rather than per-request. This eliminates CPU cost entirely.

# Pre-compress during the deployment pipeline
gzip -9 -k /var/www/html/static/app.js # creates app.js.gz
brotli -9 -k /var/www/html/static/app.js # creates app.js.br
# Nginx automatically picks the pre-compressed file
server {
location ~* \.(css|js|html|json|svg)$ {
root /var/www/html;

brotli_static on; # serve app.js.br if it exists
gzip_static on; # serve app.js.gz if it exists
}
}

Apache Gzip Configuration (mod_deflate)

# Enable mod_deflate
sudo a2enmod deflate

# /etc/apache2/conf-available/compression.conf
<IfModule mod_deflate.c>
SetOutputFilter DEFLATE
DeflateCompressionLevel 5

AddOutputFilterByType DEFLATE text/html text/css text/javascript
AddOutputFilterByType DEFLATE application/javascript application/json
AddOutputFilterByType DEFLATE application/xml image/svg+xml

# Add Vary header for cache differentiation
Header append Vary Accept-Encoding

# Skip already-compressed files
SetEnvIfNoCase Request_URI \
\.(?:gif|jpe?g|png|zip|gz|bz2|woff2)$ no-gzip dont-vary

# Legacy browser handling
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSIE !no-gzip !gzip-only-text/html
</IfModule>

Measuring Compression Effectiveness

# Compare size before and after
curl -s -H "Accept-Encoding: gzip" https://example.com/api/products \
-o /dev/null -w "Size: %{size_download} bytes, Time: %{time_total}s\n"

curl -s https://example.com/api/products \
-o /dev/null -w "Size: %{size_download} bytes, Time: %{time_total}s\n"

# Check compression headers
curl -I -H "Accept-Encoding: gzip, br" https://example.com/static/app.js
# Content-Encoding: br ← Brotli in use
# Content-Encoding: gzip ← Gzip in use

# Measure compression ratio
echo "Original size:"
wc -c /var/www/html/static/app.js

echo "Gzip size:"
gzip -c -9 /var/www/html/static/app.js | wc -c

echo "Brotli size:"
brotli -c -9 /var/www/html/static/app.js | wc -c

What to Compress — and What to Skip

# DO NOT compress these — already compressed:
# JPEG, PNG, WebP, AVIF — compressed images
# ZIP, GZ, BR, WOFF2 — compressed files
# MP4, WebM — compressed video
# Encrypted data — compression has no effect

# DO compress these:
# HTML: 50–70% savings
# CSS: 60–80% savings
# JavaScript: 60–80% savings
# JSON APIs: 60–90% savings (more repetition = better ratio)
# SVG: 70–90% savings (XML-based)

Performance Tuning Tips

http {
gzip_comp_level 1; # Lower level = less CPU for large files
gzip_min_length 10240; # Only compress files over 10 KB

# Compress dynamic responses on the fly; serve static with gzip_static
location /api/ {
proxy_pass http://backend;
gzip on;
gzip_comp_level 5;
}

location /static/ {
root /var/www;
gzip_static on; # use pre-compressed files
brotli_static on;
expires 1y;
}
}