Text-based HTTP responses — HTML, CSS, JavaScript, JSON, XML, SVG — compress dramatically with standard compression algorithms, often achieving 60–80% size reduction. Nginx supports two compression algorithms: gzip (built-in, universal browser support) and Brotli (via the ngx_brotli dynamic module, 15–20% better compression than gzip for text, supported by all modern browsers). Enabling compression is one of the highest-impact web performance optimisations: reducing a 120 KB JavaScript bundle to 30 KB over the wire means faster page loads, lower bandwidth costs, and better Core Web Vitals scores. On RHEL 9, gzip is compiled into Nginx; Brotli requires a dynamic module from EPEL. This guide covers enabling both gzip and Brotli in Nginx, configuring compression levels, excluding already-compressed files, and verifying compression is active with curl.

Prerequisites

  • Nginx installed on RHEL 9
  • EPEL repository enabled (for Brotli module)

Step 1 — Enable Gzip Compression

Add gzip configuration to the http block in /etc/nginx/nginx.conf:

vi /etc/nginx/nginx.conf
http {
    # Enable gzip
    gzip                on;
    gzip_comp_level     6;          # 1 (fastest) to 9 (best compression). 5-6 is sweet spot
    gzip_min_length     256;        # Don't compress small responses
    gzip_proxied        any;        # Compress proxied responses too
    gzip_vary           on;         # Add Vary: Accept-Encoding header
    gzip_buffers        16 8k;
    gzip_http_version   1.1;

    # Compress these MIME types
    gzip_types
        text/plain
        text/css
        text/xml
        text/javascript
        application/javascript
        application/x-javascript
        application/json
        application/xml
        application/xml+rss
        application/xhtml+xml
        application/vnd.ms-fontobject
        font/ttf
        font/opentype
        font/x-woff
        image/svg+xml
        image/x-icon;

    # Never compress these (already compressed formats)
    # gzip_types does not include image/jpeg, image/png, video/* by default
    # application/gzip and application/zip are also excluded
}
nginx -t && systemctl reload nginx

Step 2 — Verify Gzip is Working

# The Accept-Encoding header tells Nginx the client accepts gzip
curl -H "Accept-Encoding: gzip" -I http://example.com/

# Look for: Content-Encoding: gzip in the response headers

# Download and decompress to verify
curl -H "Accept-Encoding: gzip" -s http://example.com/ | file -
# Should show: gzip compressed data

Step 3 — Install the Brotli Module

# Brotli module is available in EPEL
dnf install -y epel-release
dnf install -y nginx-mod-brotli

If the EPEL package is not available for your Nginx version, build from source:

# Alternative: build ngx_brotli as a dynamic module
dnf install -y gcc make git cmake
git clone https://github.com/google/ngx_brotli
cd ngx_brotli && git submodule update --init

NGINX_VER=$(nginx -v 2>&1 | grep -oP '[0-9]+.[0-9]+.[0-9]+')
curl -O https://nginx.org/download/nginx-${NGINX_VER}.tar.gz
tar xzf nginx-${NGINX_VER}.tar.gz
cd nginx-${NGINX_VER}
./configure --with-compat --add-dynamic-module=/usr/local/src/ngx_brotli
make modules
cp objs/ngx_http_brotli_filter_module.so /usr/lib64/nginx/modules/
cp objs/ngx_http_brotli_static_module.so /usr/lib64/nginx/modules/

Step 4 — Enable Brotli in Nginx

# At the top of /etc/nginx/nginx.conf
load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;
# In the http {} block
http {
    # Brotli compression
    brotli              on;
    brotli_comp_level   6;       # 0 (fastest) to 11 (best). 6 is recommended
    brotli_static       on;      # Serve pre-compressed .br files if available
    brotli_types
        text/plain
        text/css
        text/javascript
        application/javascript
        application/json
        application/xml
        image/svg+xml;
}
nginx -t && systemctl reload nginx

Step 5 — Verify Brotli is Working

# Brotli uses the 'br' encoding token
curl -H "Accept-Encoding: br" -I https://example.com/

# Look for: Content-Encoding: br

# Compare sizes
curl -s -o /dev/null -w "%{size_download}n" http://example.com/
curl -s -H "Accept-Encoding: gzip" -o /dev/null -w "%{size_download}n" http://example.com/
curl -s -H "Accept-Encoding: br" -o /dev/null -w "%{size_download}n" https://example.com/

Step 6 — Pre-Compress Static Assets

For maximum performance, pre-compress static files at deployment time and serve the pre-compressed versions directly:

# Pre-compress CSS and JS files
find /var/www/example.com -type f ( -name "*.css" -o -name "*.js" ) | while read f; do
    gzip -9 -k "$f"           # Creates .gz file
    brotli -9 "$f"            # Creates .br file (install: dnf install brotli)
done

# Enable static module to serve pre-compressed files
# Add to server block:
location ~* .(css|js|html|json)$ {
    gzip_static  on;   # Serve .gz if exists
    brotli_static on;  # Serve .br if exists
}

Conclusion

Gzip and Brotli compression in Nginx on RHEL 9 can reduce text response sizes by 60–80%, directly improving page load speed and reducing bandwidth costs. Brotli at level 6 achieves better compression ratios than gzip for text content and is supported by all modern browsers. The combination of on-the-fly compression and pre-compressed static files provides the optimal performance vs CPU balance.

Next steps: How to Configure Nginx FastCGI Caching on RHEL 9, How to Set Up Varnish Cache as a Reverse Proxy on RHEL 9, and How to Harden Nginx: Security Headers, TLS 1.3 and OCSP Stapling on RHEL 9.