How to Enable Brotli and Gzip Compression in Nginx on RHEL 7
Compression is one of the highest-return optimizations available for web servers: it requires minimal CPU investment but can reduce text-based response sizes by 60–80%, directly improving page load times and reducing bandwidth costs. Nginx ships with the ngx_http_gzip_module built in and available on every RHEL 7 installation, making gzip compression a zero-cost configuration change. Brotli, Google’s more efficient successor to gzip, requires an additional module — ngx_brotli — which must be either compiled from source or installed via a third-party package. This tutorial covers configuring gzip compression with all recommended directives, compiling and loading the ngx_brotli dynamic module, and verifying both compression methods work correctly using curl and browser developer tools.
Prerequisites
- RHEL 7 server with
sudoaccess - Nginx installed:
sudo yum install -y nginx - For Brotli: Development tools and the libbrotli library (steps below)
curlavailable for compression testing- Internet access to clone the ngx_brotli repository from GitHub
Step 1: Configure Gzip Compression
Gzip is built into Nginx but disabled by default. All gzip directives live in the http block of /etc/nginx/nginx.conf (or an included conf.d file). Open the configuration file and add or modify the gzip block:
sudo vi /etc/nginx/nginx.conf
Add the following inside the http { } block:
http {
# Enable gzip compression
gzip on;
# Minimum response body size to compress (bytes)
# Responses smaller than this are sent uncompressed
gzip_min_length 256;
# Compression level: 1 (fastest, least compression) to 9 (slowest, most compression)
# Level 5 is a good balance between CPU usage and compression ratio
gzip_comp_level 5;
# Send Vary: Accept-Encoding header so caches store separate gzip/non-gzip copies
gzip_vary on;
# Compress responses for proxied requests (requests with Via header)
# "any" compresses regardless of the Via header value
gzip_proxied any;
# Number and size of buffers for compressed responses
gzip_buffers 16 8k;
# Minimum HTTP version for gzip (1.1 supports chunked encoding properly)
gzip_http_version 1.1;
# MIME types to compress — always include text/html; others listed here are common additions
gzip_types
text/plain
text/css
text/javascript
text/xml
text/x-component
application/javascript
application/x-javascript
application/json
application/xml
application/xml+rss
application/atom+xml
application/vnd.ms-fontobject
application/x-font-ttf
font/opentype
image/svg+xml
image/x-icon;
# Disable gzip for IE6 (known broken gzip support)
gzip_disable "MSIE [1-6].";
# ... rest of http block ...
}
Key Directive Explanations
- gzip_comp_level 5: Levels above 5 give diminishing compression gains while consuming significantly more CPU. Benchmark your own workload if you need to tune this.
- gzip_vary on: Critical for correctness with CDNs and reverse proxy caches. Without this, a cache might serve a gzip-compressed response to a client that doesn’t support gzip.
- gzip_proxied any: Ensures compression is applied even when Nginx sits behind a load balancer that adds a
Viaheader. Useoffif you only want to compress direct client responses. - gzip_types: Note that
text/htmlis always compressed and does not need to appear in this list. Never add image MIME types (jpeg, png, gif, webp) — binary images are already compressed and re-compressing them wastes CPU and can increase size.
Step 2: Test the Nginx Configuration and Reload
sudo nginx -t
# Expected: nginx: configuration file /etc/nginx/nginx.conf test is successful
sudo systemctl reload nginx
Step 3: Verify Gzip Compression with curl
# Request a compressible resource with gzip in the Accept-Encoding header
curl -H "Accept-Encoding: gzip" -I http://localhost/
# Look for:
# Content-Encoding: gzip
# Vary: Accept-Encoding
# Download and compare sizes
curl -s http://localhost/ | wc -c
curl -s -H "Accept-Encoding: gzip" http://localhost/ --compressed | wc -c
# For a JavaScript or CSS file:
curl -v --compressed -H "Accept-Encoding: gzip" http://localhost/js/app.js 2>&1 |
grep -E "Content-Encoding|Content-Length|Vary"
If you see Content-Encoding: gzip in the response headers, gzip is working correctly. The --compressed flag tells curl to decompress the response body before displaying it, so you see readable content rather than binary data.
Step 4: Install Build Dependencies for ngx_brotli
Brotli compression requires the ngx_brotli module, which depends on the libbrotli C library. On RHEL 7, this library is not in the standard repos and must be installed from EPEL or compiled alongside the module.
sudo yum install -y epel-release
sudo yum install -y
gcc gcc-c++ make cmake
git
pcre-devel
zlib-devel
openssl-devel
# Check if libbrotli is available in EPEL
yum list available | grep brotli
# If available:
sudo yum install -y brotli brotli-devel
# If not available, the ngx_brotli module will build its own libbrotli
# from a git submodule — no separate install needed in that case
Step 5: Clone ngx_brotli and Compile as a Dynamic Module
You must compile ngx_brotli against the same Nginx version that is currently installed. Determine the version first:
nginx -v
# nginx version: nginx/1.20.1
NGINX_VERSION="1.20.1" # Set to match your installed version
Clone the ngx_brotli repository and initialize its submodule:
cd /usr/local/src
sudo git clone --recurse-submodules -j4
https://github.com/google/ngx_brotli /usr/local/src/ngx_brotli
# Download the matching Nginx source
sudo wget "http://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz"
sudo tar -xzf nginx-${NGINX_VERSION}.tar.gz
cd nginx-${NGINX_VERSION}
# Use --with-compat to build a dynamically loadable module
# that is ABI-compatible with the already-installed Nginx binary
sudo ./configure
--with-compat
--add-dynamic-module=/usr/local/src/ngx_brotli
sudo make modules
# Install the compiled modules
sudo cp objs/ngx_http_brotli_filter_module.so /usr/lib64/nginx/modules/
sudo cp objs/ngx_http_brotli_static_module.so /usr/lib64/nginx/modules/
# Verify the modules are in place
ls -la /usr/lib64/nginx/modules/ | grep brotli
Step 6: Load the Brotli Modules in nginx.conf
Add the load_module directives at the top of /etc/nginx/nginx.conf, before the events block:
# /etc/nginx/nginx.conf — top of file
load_module modules/ngx_http_brotli_filter_module.so;
load_module modules/ngx_http_brotli_static_module.so;
events {
worker_connections 1024;
}
http {
# Gzip config (from Step 1) ...
# Brotli compression settings
brotli on;
# Compression level: 0 (no compression) to 11 (maximum compression)
# Level 6 is recommended for dynamic content — balances ratio and CPU
brotli_comp_level 6;
# Enable static Brotli serving — serves pre-compressed .br files if they exist
brotli_static on;
# Minimum response size to compress with Brotli
brotli_min_length 256;
# Window size for compression (affects memory usage and compression ratio)
brotli_window 512k;
# MIME types to compress with Brotli (same list as gzip_types)
brotli_types
text/plain
text/css
text/javascript
text/xml
application/javascript
application/x-javascript
application/json
application/xml
application/xml+rss
application/atom+xml
image/svg+xml;
# ... server blocks ...
}
Step 7: Test and Reload Nginx
sudo nginx -t
# Expected: nginx: configuration file /etc/nginx/nginx.conf test is successful
sudo systemctl reload nginx
Step 8: Verify Brotli Compression with curl
# Request with Brotli in the Accept-Encoding header
# curl does not natively decompress Brotli, so inspect headers only
curl -v -H "Accept-Encoding: br,gzip" http://localhost/ 2>&1 |
grep -E "< Content-Encoding|< Vary"
# Expected output:
# < Content-Encoding: br
# < Vary: Accept-Encoding
# If the server returns gzip even when br is offered, confirm that:
# 1. The brotli modules loaded successfully (check error log)
# 2. The response MIME type is in brotli_types
sudo journalctl -u nginx --since "5 minutes ago" | grep -i brotli
sudo grep "brotli|emerg|crit" /var/log/nginx/error.log | tail -20
Step 9: Browser Verification
Modern browsers send Accept-Encoding: br, gzip, deflate automatically. To verify Brotli is being served to real browser clients:
- Open Chrome or Firefox DevTools (F12) and navigate to the Network tab.
- Load your page and click on any text or JavaScript resource.
- In the Response Headers panel, look for
Content-Encoding: br. - In the Request Headers panel, confirm
Accept-Encoding: br, gzip, deflate, zstdwas sent.
Step 10: Pre-compressed Static Files with brotli_static
For static assets with high traffic, pre-compressing files offline and serving the pre-built .br files eliminates per-request CPU overhead entirely. With brotli_static on, Nginx serves a pre-existing file.js.br when a client requests file.js with Brotli support.
# Install the brotli command-line tool
sudo yum install -y brotli # or build from source if not in EPEL
# Pre-compress your static assets
cd /usr/share/nginx/html
for f in *.js *.css *.html; do
brotli --quality=11 --output="${f}.br" "$f"
echo "Compressed: $f → ${f}.br"
done
ls -lh *.br
Nginx will automatically select the .br file when a compatible client requests the original file — no URL changes or rewrite rules are needed.
Complete Compression Block Reference
http {
# Gzip
gzip on;
gzip_comp_level 5;
gzip_min_length 256;
gzip_vary on;
gzip_proxied any;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css text/javascript application/javascript
application/json application/xml image/svg+xml;
gzip_disable "MSIE [1-6].";
# Brotli
brotli on;
brotli_comp_level 6;
brotli_min_length 256;
brotli_window 512k;
brotli_static on;
brotli_types text/plain text/css text/javascript application/javascript
application/json application/xml image/svg+xml;
}
Enabling both gzip and Brotli simultaneously is safe and recommended: Nginx negotiates the best format based on the client’s Accept-Encoding header, serving Brotli to modern browsers and gzip to older clients automatically. For typical web assets — HTML, CSS, JavaScript, and JSON API responses — the combination of gzip level 5 for compatibility and Brotli level 6 for modern clients delivers the best balance of compression ratio, CPU usage, and broad client support. Pre-compressing static assets with Brotli at level 11 offline for your highest-traffic files takes this a step further, effectively making compression free at request-serve time.