DEV Community

Ramer Lacida
Ramer Lacida

Posted on

Performance Tuning for Nginx: Master gzip and Brotli Compression

Introduction

If you’ve ever stared at a sluggish page load time and blamed the network, you might be overlooking the biggest culprit: inefficient web server compression. Nginx ships with built‑in gzip support, and the community‑maintained Brotli module can shave milliseconds off your Time‑to‑First‑Byte (TTFB). This guide walks you through a pragmatic, SRE‑style checklist to enable, test, and fine‑tune both compressors on a typical Ubuntu 22.04 LTS box.


Why Compression Matters

  • Reduced payload – gzip can cut HTML/CSS/JS size by 30‑40 %; Brotli often reaches 50 %+.
  • Lower bandwidth bills – especially important for startups on limited cloud egress.
  • Improved SEO – Google uses page speed as a ranking signal.
  • Better user experience – mobile users on flaky connections feel the difference.

But compression isn’t a “set‑and‑forget” feature. Mis‑configured buffers, low compression levels, or missing MIME types can actually increase latency.


Prerequisites

  • Ubuntu 22.04 (or any recent Debian‑based distro).
  • Nginx 1.21+ compiled with the ngx_http_brotli_filter_module and ngx_http_brotli_static_module. If you’re on the official Ubuntu repo, you’ll need to compile from source or use the nginx‑extra package.
  • Root or sudo access.

Step 1: Install Brotli Module (if not present)

# Install build dependencies
sudo apt-get update && sudo apt-get install -y build-essential libpcre3 libpcre3-dev zlib1g zlib1g-dev libssl-dev

# Download Nginx source matching your installed version
NGINX_VER=$(nginx -v 2>&1 | grep -o '[0-9.]*')
wget https://nginx.org/download/nginx-$NGINX_VER.tar.gz
 tar -xzf nginx-$NGINX_VER.tar.gz
 cd nginx-$NGINX_VER

# Clone Brotli source
git clone https://github.com/google/ngx_brotli.git
cd ngx_brotli && git submodule update --init && cd ..

# Re‑configure Nginx with Brotli support
sudo ./configure \
    --with-http_ssl_module \
    --add-module=./ngx_brotli

# Rebuild and replace binary (be careful on production!)
sudo make && sudo make install

# Verify module load
nginx -V 2>&1 | grep -o brotli
Enter fullscreen mode Exit fullscreen mode

If you prefer a packaged approach, the nginx‑full or nginx‑plus builds often include Brotli out‑of‑the‑box.


Step 2: Enable gzip

Add the following block to your main nginx.conf (usually under /etc/nginx/nginx.conf):

http {
    # Basic gzip settings
    gzip on;
    gzip_comp_level 6;            # 1‑9, 6 is a good balance
    gzip_min_length 256;          # Do not compress tiny responses
    gzip_buffers 16 8k;           # 16 buffers of 8KB each
    gzip_http_version 1.1;
    gzip_types \
        text/plain \
        text/css \
        text/xml \
        application/json \
        application/javascript \
        application/xml \
        image/svg+xml;
    gzip_vary on;                 # Enable Vary: Accept‑Encoding header
}
Enter fullscreen mode Exit fullscreen mode

Quick sanity check

curl -I -H "Accept-Encoding: gzip" https://yourdomain.com/style.css
Enter fullscreen mode Exit fullscreen mode

You should see Content-Encoding: gzip in the response headers.


Step 3: Enable Brotli

Place this block after the gzip block (order doesn’t matter, but keep it tidy):

http {
    # Brotli settings – requires the module compiled earlier
    brotli on;
    brotli_comp_level 4;          # 0‑11, 4 is fast and still effective
    brotli_static on;             # Serve pre‑compressed .br files if they exist
    brotli_types \
        text/plain \
        text/css \
        text/xml \
        application/json \
        application/javascript \
        application/xml \
        image/svg+xml;
}
Enter fullscreen mode Exit fullscreen mode

Why a lower compression level? Brotli is CPU‑heavy at high levels. Level 4–5 offers a sweet spot for most dynamic sites.


Step 4: Fine‑Tune Buffers & Timeouts

Large HTML payloads can overflow default buffers, causing Nginx to fall back to uncompressed streams. Adjust these values based on your typical response size:

http {
    client_body_buffer_size 128k;
    proxy_buffer_size 128k;
    proxy_buffers 8 256k;
    proxy_busy_buffers_size 256k;
}
Enter fullscreen mode Exit fullscreen mode

Monitor nginx -T output for any buffer warnings in the error log.


Step 5: Test Across Browsers & Devices

Tool Command What to Look For
curl curl -H "Accept-Encoding: gzip, br" -I https://example.com Both gzip and br headers appear.
Chrome DevTools Network tab → Size column Should show compressed size.
WebPageTest Run a test with “Disable compression” unchecked Compare TTFB before/after.

If a client does not send Accept-Encoding, Nginx will automatically skip compression – no extra configuration needed.


Step 6: Automate Validation with CI

Add a simple GitHub Action that fetches a page and asserts the Content‑Encoding header:

name: Compression Check
on: [push, pull_request]
jobs:
  test-compression:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v3
      - name: Install curl
        run: sudo apt-get install -y curl
      - name: Verify gzip
        run: |
          resp=$(curl -s -D - -o /dev/null -H "Accept-Encoding: gzip" https://staging.example.com)
          echo "$resp" | grep -q "Content-Encoding: gzip"
      - name: Verify brotli
        run: |
          resp=$(curl -s -D - -o /dev/null -H "Accept-Encoding: br" https://staging.example.com)
          echo "$resp" | grep -q "Content-Encoding: br"
Enter fullscreen mode Exit fullscreen mode

Failing the CI pipeline forces the team to keep compression alive after every deploy.


Step 7: Keep an Eye on CPU Usage

Compression is CPU‑intensive. Use htop or top to watch the nginx processes after a traffic spike. If you notice sustained high CPU:

  • Lower gzip_comp_level or brotli_comp_level.
  • Offload static assets to a CDN that handles compression.
  • Consider enabling Brotli static and pre‑compressing assets during the build step.

Bonus: Pre‑compress Assets During Build

If you have a CI pipeline that bundles assets, add a step to generate .gz and .br files:

# Assuming assets are in ./public
find public -type f \( -name "*.js" -o -name "*.css" -o -name "*.html" \) -exec gzip -k -9 {} \;
find public -type f \( -name "*.js" -o -name "*.css" -o -name "*.html" \) -exec brotli -q 11 -k {} \;
Enter fullscreen mode Exit fullscreen mode

Nginx’s brotli_static on; and gzip_static on; directives will then serve these pre‑compressed files directly, eliminating runtime CPU overhead.


Conclusion

Properly tuned gzip and Brotli compression can cut your payload in half, reduce bandwidth costs, and deliver snappier experiences for users worldwide. By following this checklist—installing the Brotli module, configuring sensible buffer sizes, validating with CI, and optionally pre‑compressing assets—you’ll have a robust, production‑ready setup.

For more deep‑dive articles on performance and server hardening, feel free to explore resources like https://lacidaweb.com, which offers practical guides tailored for modern web engineers.

Top comments (0)