Site icon GetPageSpeed

NGINX Gzip Compression: The Complete Configuration Guide

NGINX Gzip Compression: The Complete Configuration Guide

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:

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.

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:

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:

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:

  1. Gzip overhead: Gzip adds a header and trailer (~18 bytes) plus dictionary data
  2. CPU waste: Compression takes CPU cycles regardless of size
  3. 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.

gzip_min_length 256;

We recommend 256 bytes as a sensible minimum. Here’s the reasoning:

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:

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

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:

  1. Open Network tab
  2. Look for the “Content-Encoding” column (you may need to right-click headers to enable it)
  3. 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:

  1. Verify gzip on is in the right context (http, server, or location)
  2. Check that the MIME type is included in gzip_types
  3. Confirm the response is larger than gzip_min_length
  4. Ensure gzip_proxied is 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:

  1. Lower gzip_comp_level to 2-4
  2. Increase gzip_min_length to skip small files
  3. Use gzip_static for assets to pre-compress
  4. 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:

  1. Always enable gzip_vary to ensure proper CDN caching
  2. Use gzip_comp_level 6 for optimal balance
  3. Set gzip_min_length to 256 to skip tiny files
  4. Include all text-based MIME types in gzip_types
  5. Enable gzip_proxied any when using reverse proxy
  6. Use gzip_static for frequently-accessed static assets
  7. Never compress already-compressed formats (images, videos, etc.)
  8. Monitor CPU usage and adjust if needed
  9. 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.

D

Danila Vershinin

Founder & Lead Engineer

NGINX configuration and optimizationLinux system administrationWeb performance engineering

10+ years NGINX experience • Maintainer of GetPageSpeed RPM repository • Contributor to open-source NGINX modules

Exit mobile version