Text files — HTML, CSS, JavaScript, JSON, SVG — compress extremely well, often to 20–30% of their size. Compressing them on the way out makes pages load faster, especially on mobile connections, and cuts your bandwidth use. Nginx supports gzip out of the box and Brotli through a module.

Enable gzip

Ubuntu’s nginx.conf already contains gzip on; but compresses only text/html by default. Create /etc/nginx/conf.d/gzip.conf:

gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_min_length 256;
gzip_types
    text/plain
    text/css
    text/xml
    text/javascript
    application/javascript
    application/json
    application/ld+json
    application/manifest+json
    application/rss+xml
    application/atom+xml
    application/xml
    image/svg+xml
    font/ttf
    font/otf
    application/vnd.ms-fontobject;

If nginx.conf already has a gzip on; line, either remove it there or omit it here — a duplicate directive in the same context makes nginx -t fail.

sudo nginx -t && sudo systemctl reload nginx

What the settings do:

  • gzip_types — MIME types to compress. text/html is always included, so don’t list it (Nginx warns about duplicates).
  • gzip_comp_level 5 — levels 1–9; above 5–6, files barely shrink further while CPU cost keeps rising.
  • gzip_min_length 256 — tiny responses aren’t worth compressing.
  • gzip_vary on — adds Vary: Accept-Encoding, so caches store compressed and uncompressed versions separately.
  • gzip_proxied any — also compress responses to requests that came through a proxy or CDN.

Don’t compress JPEG, PNG, WebP, AVIF, MP4, WOFF2 or ZIP files — they’re already compressed, and gzip just wastes CPU.

Add Brotli

Brotli typically produces files 15–25% smaller than gzip for text, and every current browser supports it over HTTPS. Nginx needs the ngx_brotli module.

On Debian and recent Ubuntu, packaged dynamic modules exist:

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

(Package names and availability vary by release; if they’re missing, the nginx.org packages or a build of the Google ngx_brotli module against your exact Nginx version are the alternatives.)

The packages enable the modules automatically via /etc/nginx/modules-enabled/. Then configure, e.g. in /etc/nginx/conf.d/brotli.conf:

brotli on;
brotli_comp_level 5;
brotli_min_length 256;
brotli_types
    text/plain text/css text/xml text/javascript
    application/javascript application/json application/ld+json
    application/manifest+json application/rss+xml application/xml
    image/svg+xml font/ttf font/otf application/vnd.ms-fontobject;

Keep gzip enabled too: Nginx serves Brotli to clients that send Accept-Encoding: br and gzip to the rest.

Pre-compressed static files

Compressing on every request costs CPU. For static assets that don’t change between deploys, compress once at build time and let Nginx serve the ready-made file:

location /assets/ {
    gzip_static on;      # serves app.js.gz if it exists
    brotli_static on;    # serves app.js.br if it exists (needs the static module)
    expires 1y;
    add_header Cache-Control "public, immutable";
}

Create the files during your build:

find dist/assets -type f \( -name '*.js' -o -name '*.css' -o -name '*.svg' \) \
  -exec gzip -k -9 {} \; -exec brotli -k -q 11 {} \;

With pre-compressed files you can use maximum compression levels, since the cost is paid once.

Verify

curl -s -o /dev/null -w "%{size_download} bytes\n" https://example.com/styles.css
curl -s -o /dev/null -w "%{size_download} bytes\n" -H "Accept-Encoding: gzip" https://example.com/styles.css
curl -sI -H "Accept-Encoding: br" https://example.com/styles.css | grep -i content-encoding

The second number should be much smaller, and the last command should print content-encoding: br. In browser developer tools, the Network tab shows Content-Encoding per request.

Compression and proxies/CDNs

  • If you use Cloudflare or another CDN, it compresses for visitors itself; your origin compression still saves bandwidth between origin and CDN.
  • When Nginx proxies to an app that already compresses, Nginx passes the compressed response through. Usually it’s better to turn compression off in the app and let Nginx do it.

Security note: BREACH

Compressing HTTPS responses that contain both secrets (CSRF tokens) and attacker-controlled input can, in theory, allow the BREACH attack. Most frameworks mitigate it by masking tokens per request. It’s not a reason to disable compression for typical sites.

Results to expect

File Original gzip (5) Brotli (5)
Framework CSS 190 KB ~26 KB ~21 KB
JS bundle 450 KB ~140 KB ~120 KB
HTML page 60 KB ~12 KB ~10 KB

Figures vary, but compression is one of the cheapest speed wins available. Combine it with browser caching and see website speed optimization for more. Faster pages also mean less transfer — welcome on any VPS plan.

  • Nginx
  • Performance
  • Compression