yum upgrades for production use, this is the repository for you.
Active subscription is required.
NGINX gzip compression is one of the most effective ways to reduce bandwidth usage and speed up your website. When configured correctly, nginx compression can reduce the size of text-based responses by 70-90%, resulting in faster page loads and improved Core Web Vitals scores.
Yet many administrators either skip gzip configuration entirely or use suboptimal settings. This comprehensive guide covers everything you need to know about nginx gzip compression, from basic setup to advanced optimization techniques based on NGINX source code analysis.
Why NGINX Gzip Compression Matters
Before diving into configuration, let’s understand why nginx gzip is essential for modern web performance:
- Reduced bandwidth costs: Compressed responses use significantly less bandwidth
- Faster page loads: Smaller files transfer more quickly over the network
- Improved SEO: Google considers page speed as a ranking factor
- Better user experience: Users on slow connections benefit the most
- Lower server load: Less data to send means reduced network I/O
According to HTTP Archive data, enabling gzip compression can reduce the total transfer size of a typical web page by 60-80%. For text-heavy sites with lots of JavaScript and CSS, the savings can be even greater. Combine this with proper browser caching for maximum performance improvement.
Enabling NGINX Gzip Compression
By default, nginx does not enable gzip compression. You must explicitly configure it in your nginx configuration file. Here’s how to enable basic nginx gzip compression:
gzip on;
This single directive enables gzip compression in nginx. However, with default settings, nginx only compresses text/html responses. For a complete nginx compression setup, you need additional configuration.
Complete NGINX Gzip Configuration
Here’s a production-ready nginx gzip configuration that covers all common MIME types:
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 256;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/x-javascript
application/json
application/xml
application/rss+xml
application/atom+xml
image/svg+xml;
Save this configuration to /etc/nginx/conf.d/gzip.conf and reload nginx:
nginx -t && systemctl reload nginx
Let’s examine each directive in detail.
Understanding gzip_types
The gzip_types directive specifies which MIME types should be compressed. This is one of the most important nginx gzip settings because it determines what content gets compressed.
Default Behavior
According to the NGINX source code, gzip_types defaults to text/html only. This means without explicit configuration, nginx gzip compression only applies to HTML files:
ngx_str_t ngx_http_html_default_types[] = {
ngx_string("text/html"),
ngx_null_string
};
This conservative default exists because not all content types benefit from compression. Binary files like images and videos are already compressed and may actually increase in size when gzip is applied.
Recommended gzip_types Configuration
For optimal nginx compression, include all text-based content types:
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/x-javascript
application/json
application/xml
application/xml+rss
application/rss+xml
application/atom+xml
application/vnd.ms-fontobject
application/x-font-ttf
application/x-font-opentype
application/x-font-truetype
font/opentype
font/eot
font/ttf
font/otf
font/woff
font/woff2
image/svg+xml
image/x-icon
image/vnd.microsoft.icon;
What NOT to Include in gzip_types
Never add already-compressed formats to gzip_types:
- Images: JPEG, PNG, GIF, WebP (already compressed)
- Videos: MP4, WebM (already compressed)
- Archives: ZIP, GZIP, BZIP2 (already compressed)
- PDFs: Already use internal compression
Compressing already-compressed content wastes CPU cycles and can actually make files larger due to gzip overhead.
Important Note About text/html
You should never include text/html in your gzip_types directive. The NGINX source code always includes text/html automatically when gzip is enabled. Adding it explicitly will result in a warning:
nginx: [warn] duplicate MIME type "text/html" in /etc/nginx/conf.d/gzip.conf
Understanding gzip_comp_level
The gzip_comp_level directive controls the compression level, ranging from 1 (fastest, least compression) to 9 (slowest, best compression).
Source Code Analysis
The NGINX source code defines the valid range for gzip_comp_level:
static ngx_conf_num_bounds_t ngx_http_gzip_comp_level_bounds = {
ngx_conf_check_num_bounds, 1, 9
};
The default value is 1 (minimum compression):
ngx_conf_merge_value(conf->level, prev->level, 1);
Choosing the Right Compression Level
Higher compression levels provide diminishing returns while dramatically increasing CPU usage:
| Level | Compression Ratio | CPU Usage | Recommendation |
|---|---|---|---|
| 1 | ~60% | Very Low | Not recommended (too low) |
| 2-3 | ~65% | Low | High-traffic sites |
| 4-5 | ~70% | Medium | Balanced choice |
| 6 | ~75% | Medium | Best balance |
| 7-8 | ~78% | High | Diminishing returns |
| 9 | ~80% | Very High | Not recommended |
Our Recommendation
Use gzip_comp_level 6 for the best balance between compression ratio and CPU usage:
gzip_comp_level 6;
At level 6, you get approximately 75% of the maximum possible compression while using only about 50% of the CPU time compared to level 9. The difference between level 6 and level 9 is typically only 2-5% more compression, but level 9 can use 3-4x more CPU.
When to Use Lower Compression Levels
Consider using levels 2-4 if:
- Your server is CPU-constrained
- You serve extremely high traffic volumes
- You use nginx as a reverse proxy with many backends
- Response times are more critical than bandwidth savings
Memory Considerations
Higher compression levels require more memory per connection. According to NGINX source code comments:
/*
* We preallocate a memory for zlib in one buffer (200K-400K), this
* decreases a number of malloc() and free() calls and also probably
* decreases a number of syscalls (sbrk()/mmap() and so on).
*/
For high-traffic servers handling thousands of concurrent connections, this memory usage can become significant. Each connection using gzip compression requires approximately 200-400 KB of memory for the compression buffer.
Understanding gzip_min_length
The gzip_min_length directive sets the minimum response size (in bytes) for nginx to apply gzip compression. Responses smaller than this threshold are sent uncompressed.
Default Value and Source Code
The NGINX source code reveals the default minimum length:
ngx_conf_merge_value(conf->min_length, prev->min_length, 20);
The default is just 20 bytes, which is far too low for production use.
Why gzip_min_length Matters
Compressing very small files is counterproductive because:
- Gzip overhead: Gzip adds a header and trailer (~18 bytes) plus dictionary data
- CPU waste: Compression takes CPU cycles regardless of size
- No benefit: Tiny files may actually become larger after compression
For example, a 50-byte response might compress to 45 bytes of compressed data, but with the 18-byte gzip header/trailer, the total becomes 63 bytes—larger than the original.
Recommended gzip_min_length Values
gzip_min_length 256;
We recommend 256 bytes as a sensible minimum. Here’s the reasoning:
- Files under 256 bytes rarely compress well enough to offset gzip overhead
- Modern networks have MTU sizes around 1500 bytes, so small responses fit in a single packet anyway
- CPU savings from skipping tiny files adds up on high-traffic sites
Some administrators use higher values like 1000 or 1024 bytes for very CPU-constrained environments.
How NGINX Determines Response Size
NGINX checks the Content-Length header to determine response size. From the source code:
if (r->headers_out.content_length_n != -1
&& r->headers_out.content_length_n < conf->min_length)
{
return ngx_http_next_header_filter(r);
}
If the response has no Content-Length header (chunked transfer encoding), nginx cannot check the minimum length and will attempt compression. This is why it’s important to set appropriate proxy buffer sizes when using proxy_pass.
Additional NGINX Gzip Directives
Beyond the core three directives, several other settings affect nginx gzip compression behavior.
gzip_vary
gzip_vary on;
This directive adds Vary: Accept-Encoding to responses. This header tells caching proxies and CDNs that the response varies based on the client’s Accept-Encoding header. Without this, a proxy might cache the gzip-compressed version and serve it to clients that don’t support gzip.
Always enable gzip_vary when using gzip compression.
gzip_proxied
gzip_proxied any;
This directive controls compression for proxied requests (when nginx acts as a reverse proxy). Options include:
off– Never compress proxied responsesany– Compress all proxied responsesexpired– Compress if Expires header prevents cachingno-cache– Compress if Cache-Control contains “no-cache”no-store– Compress if Cache-Control contains “no-store”private– Compress if Cache-Control contains “private”no_last_modified– Compress if no Last-Modified headerno_etag– Compress if no ETag headerauth– Compress if Authorization header present
For most setups, gzip_proxied any is the right choice. It ensures all proxied responses are compressed regardless of cache headers.
gzip_disable
gzip_disable "MSIE [1-6]\.";
This directive disables gzip for specific User-Agents based on a regex pattern. The classic use case is disabling gzip for Internet Explorer 6 and earlier, which had buggy gzip support.
In 2026 and beyond, you likely don’t need this directive since ancient browsers are virtually extinct. However, it’s still useful if you encounter specific user agents with compression issues.
gzip_buffers
gzip_buffers 16 8k;
This directive configures the number and size of buffers used for gzip compression. The default depends on your system’s page size. Most administrators can leave this at default values unless they’re doing advanced tuning.
gzip_http_version
gzip_http_version 1.1;
Sets the minimum HTTP version required for gzip responses. The default is 1.1, which is correct for modern deployments. HTTP/1.0 clients without proper gzip support are extremely rare today.
NGINX Gzip Static: Pre-compressed Files
The gzip_static module allows nginx to serve pre-compressed .gz files instead of compressing on-the-fly:
gzip_static on;
When a client requests /style.css and sends Accept-Encoding: gzip, nginx first checks if /style.css.gz exists. If it does, nginx serves the pre-compressed file directly, saving CPU cycles.
Enabling gzip_static
First, verify the module is compiled in:
nginx -V 2>&1 | grep -o 'http_gzip_static_module'
If you see output, the module is available. The standard nginx packages on RHEL, Rocky Linux, and AlmaLinux include this module by default.
Creating Pre-compressed Files
You can create pre-compressed files using gzip:
gzip -k -9 /var/www/html/assets/*.css
gzip -k -9 /var/www/html/assets/*.js
The -k flag keeps the original file, and -9 uses maximum compression since we’re doing this offline and CPU time doesn’t matter.
For better compression, consider using Zopfli, which produces smaller gzip-compatible files:
zopfli /var/www/html/assets/*.css
zopfli /var/www/html/assets/*.js
gzip_static Options
gzip_static off– Disabled (default)gzip_static on– Serve .gz files if they exist and client accepts gzipgzip_static always– Always serve .gz files (for pre-compressed-only deployments)
Verifying NGINX Gzip Compression
After configuring nginx gzip compression, verify it’s working correctly.
Using curl
curl -sI -H "Accept-Encoding: gzip" https://example.com/ | grep -i -E "(Content-Encoding|Vary)"
You should see:
Content-Encoding: gzip
Vary: Accept-Encoding
Checking Compression Ratio
To see the actual compression ratio:
curl -s -H "Accept-Encoding: gzip" https://example.com/ | wc -c
curl -s https://example.com/ | wc -c
Compare the two sizes to calculate your compression ratio.
Using Browser DevTools
In Chrome or Firefox Developer Tools:
- Open Network tab
- Look for the “Content-Encoding” column (you may need to right-click headers to enable it)
- Verify responses show “gzip”
Checking Configuration
View your complete nginx configuration including gzip settings:
nginx -T | grep -A 20 gzip
Common NGINX Gzip Issues and Solutions
Issue: Compression Not Working
Symptom: No Content-Encoding: gzip header in responses.
Solutions:
- Verify
gzip onis in the right context (http, server, or location) - Check that the MIME type is included in
gzip_types - Confirm the response is larger than
gzip_min_length - Ensure
gzip_proxiedis set if using reverse proxy
Issue: Double Compression
Symptom: Backend already compresses, nginx tries to compress again.
Solution: Check if backend is setting Content-Encoding. If so, nginx won’t compress again (by design). The source code skips compression if:
if (r->headers_out.content_encoding
&& r->headers_out.content_encoding->value.len)
{
return ngx_http_next_header_filter(r);
}
Issue: Vary Header Missing
Symptom: CDN caching issues, wrong version served to some clients.
Solution: Ensure gzip_vary on is set. This is critical for proper CDN behavior.
Issue: High CPU Usage
Symptom: Server CPU spikes during high traffic.
Solutions:
- Lower
gzip_comp_levelto 2-4 - Increase
gzip_min_lengthto skip small files - Use
gzip_staticfor assets to pre-compress - Consider moving compression to a CDN
Performance Comparison: Gzip Levels Benchmark
Here are typical compression results for a 100KB JavaScript file:
| Level | Compressed Size | Compression Time | Ratio |
|---|---|---|---|
| 1 | 32 KB | 1.2 ms | 68% |
| 4 | 28 KB | 2.1 ms | 72% |
| 6 | 26 KB | 3.4 ms | 74% |
| 9 | 25 KB | 12.1 ms | 75% |
As you can see, the jump from level 1 to 6 gains 6% more compression while only adding 2.2ms. Going from level 6 to 9 gains only 1% more compression but adds 8.7ms—a poor tradeoff.
Best Practices for NGINX Gzip Compression
Based on our analysis of the NGINX source code and real-world testing, here are our recommendations:
- Always enable gzip_vary to ensure proper CDN caching
- Use gzip_comp_level 6 for optimal balance
- Set gzip_min_length to 256 to skip tiny files
- Include all text-based MIME types in gzip_types
- Enable gzip_proxied any when using reverse proxy
- Use gzip_static for frequently-accessed static assets
- Never compress already-compressed formats (images, videos, etc.)
- Monitor CPU usage and adjust if needed
- Test compression with curl after configuration changes
Complete Production Configuration
Here’s our recommended complete nginx gzip configuration for production use:
# /etc/nginx/conf.d/gzip.conf
# Enable gzip compression
gzip on;
# Enable Vary header for proper caching
gzip_vary on;
# Compress responses for proxied requests
gzip_proxied any;
# Compression level (1-9, 6 is optimal)
gzip_comp_level 6;
# Minimum size for compression (skip tiny files)
gzip_min_length 256;
# MIME types to compress
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/x-javascript
application/json
application/xml
application/rss+xml
application/atom+xml
application/vnd.ms-fontobject
application/x-font-ttf
application/x-font-opentype
font/opentype
font/eot
font/ttf
font/woff
font/woff2
image/svg+xml
image/x-icon;
# Enable serving pre-compressed files
gzip_static on;
Apply this configuration and test:
nginx -t && systemctl reload nginx
Conclusion
Proper nginx gzip compression configuration is essential for web performance. By understanding the key directives—gzip_types, gzip_comp_level, and gzip_min_length—you can significantly reduce bandwidth usage while maintaining fast response times.
Remember that compression is a trade-off between CPU usage and bandwidth savings. The recommended settings in this guide provide an optimal balance for most websites, but always monitor your specific situation and adjust as needed.
With the configuration provided in this guide, you should see 60-80% reduction in transfer sizes for text-based content, resulting in faster page loads and happier users.
