Every byte your server sends costs time. On a fast connection it's barely noticeable. On a mobile network, or when your server is handling hundreds of simultaneous requests, those extra bytes stack up fast. Gzip compression is one of the simplest server-side wins you can make, and Nginx gives you fine-grained control over exactly how it works.
Most default Nginx installations ship with gzip either disabled or barely configured. Getting it right takes about 10 minutes, and the payoff shows up immediately in your response sizes and Time to First Byte. We've seen HTML pages shrink by 70-80% and JavaScript files compress down to a third of their original size with nothing more than a proper gzip configuration.
How Nginx Gzip Compression Actually Works
When a browser makes a request, it sends an Accept-Encoding: gzip, deflate, br header. This tells the server it can handle compressed responses. Nginx checks this header and, if gzip is enabled, compresses the response body before sending it. The browser decompresses it transparently. Your application code doesn't change at all.
The key tradeoff is CPU time against bandwidth. Compression takes a small amount of processing power on the server side. For most sites, this is a net win - the bandwidth savings far outweigh the CPU cost, especially on text-heavy responses. The exception is already-compressed files like JPEG images, ZIP archives, or MP4 videos. Trying to gzip those wastes CPU and barely shrinks the file.
The Core Nginx Gzip Configuration Block
All gzip settings live in your nginx.conf file, typically in the http block so they apply globally. Here's a solid starting configuration:
http { gzip on; gzip_vary on; gzip_proxied any; gzip_comp_level 6; gzip_min_length 256; gzip_buffers 16 8k; gzip_http_version 1.1; gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/x-javascript application/xml application/xml+rss application/atom+xml image/svg+xml font/truetype font/opentype application/vnd.ms-fontobject; }Let's break down the directives that actually matter.
gzip_comp_level: The Setting Most People Get Wrong
The compression level runs from 1 (fastest, least compression) to 9 (slowest, most compression). Many guides tell you to set it to 9. Don't.
Level 9 uses significantly more CPU than level 6, but the size difference is minimal - usually less than 2-3% smaller than level 6. Level 6 is the sweet spot. You get around 70-80% compression on typical HTML and CSS files without hammering your CPU on every request. Nginx performance tuning is often about removing bottlenecks rather than chasing marginal gains, and this is a good example of that principle.
gzip_min_length: Don't Compress Tiny Responses
Compressing a 50-byte response is pointless. The gzip header overhead alone is around 20 bytes, so you barely save anything and you've burned CPU doing it. Setting gzip_min_length 256 tells Nginx to only compress responses larger than 256 bytes. For very small API responses or tiny text files, this directive saves real processing time.
gzip_vary: Critical for Caching Layers
Always enable gzip_vary on. This adds a Vary: Accept-Encoding header to responses, which tells CDNs and proxy caches to store separate versions of each resource - one compressed, one not. Without this, a CDN might cache the compressed version and serve it to a browser that said it couldn't handle gzip. That browser then receives garbled data.
gzip_proxied: Handling Requests Through Proxies
If your Nginx is sitting behind a load balancer or CDN, requests arrive with a Via header. By default, Nginx won't gzip responses to proxied requests. Setting gzip_proxied any tells it to compress regardless. In most setups this is what you want.
Which MIME Types to Include (And Which to Skip)
The gzip_types directive controls exactly which content types get compressed. Nginx always compresses text/html regardless of this list, but everything else you have to specify explicitly.
Compress these file types - they're mostly text and compress extremely well:
- CSS and JavaScript files (often 60-80% smaller)
- JSON API responses (often 70-85% smaller)
- SVG images (often 50-70% smaller)
- XML feeds and sitemaps
- Web fonts in text-based formats
Skip these entirely:
- JPEG, PNG, WebP, AVIF images - already compressed
- MP4, WebM, and other video formats
- ZIP, GZ, and other archive files
- PDF files
- WOFF2 font files - already compressed internally
Trying to gzip a JPEG doesn't just fail to save space - it can actually make the file slightly larger while burning CPU. Leave binary formats alone.
Testing Your Gzip Configuration
After reloading Nginx, confirm compression is working with curl:
curl -H "Accept-Encoding: gzip" -I https://yourdomain.comYou should see Content-Encoding: gzip in the response headers. If you don't, double-check that your config block is in the right context (the http block, not inside a location block where it might be overridden).
For a more complete view, run your URL through Google PageSpeed Insights or check the Network tab in Chrome DevTools. Look at the size column - you'll see the transferred size versus the actual size. A CSS file that's 80KB uncompressed showing as 18KB transferred means gzip is working correctly.
Going Further With Gzip Static
If you want to push nginx performance tuning even further, look at the ngx_http_gzip_static_module. Instead of compressing files on every request, you pre-compress them and store .gz versions on disk. Nginx serves the pre-compressed file directly when a client supports gzip, skipping the compression step entirely.
location ~* \.(js|css|html|xml|json|svg)$ { gzip_static on; expires max; add_header Cache-Control public; }This approach works well for static asset pipelines where files are built and deployed. Your build process creates app.js and app.js.gz, and Nginx serves whichever is appropriate. Zero runtime CPU cost for compression on those files. We already covered the broader topic of worker process configuration in How to Configure Nginx Worker Processes for Maximum Throughput, and static gzip fits naturally into that same performance layer.
Gzip and Brotli: Should You Use Both?
Brotli is a newer compression algorithm from Google that typically achieves 15-25% better compression than gzip on text files, with similar CPU cost. Modern browsers all support it. If you have the ngx_brotli module available, enabling it alongside gzip gives you the best of both worlds - Brotli for modern browsers, gzip as a fallback for older ones.
The configuration pattern mirrors gzip:
brotli on; brotli_comp_level 6; brotli_types text/plain text/css application/json application/javascript text/xml application/xml image/svg+xml;Nginx sends Brotli when the browser requests it, and falls back to gzip otherwise. No extra work on your end once it's configured.
What to Expect After Enabling Gzip
The gains are real and immediate. A typical page with 200KB of uncompressed HTML, CSS, and JS usually transfers around 50-70KB after gzip. That's a 65-75% reduction in transfer size. On a slow mobile connection at 5 Mbps, that's the difference between a 320ms transfer and a 90ms one - before you even factor in DNS, TCP handshakes, or rendering.
TTFB (Time to First Byte) doesn't change from gzip alone, since that measures server processing time before the response starts flowing. But total page load time drops noticeably because the response body arrives faster. If you're working on TTFB separately, Why Your Time to First Byte Is Costing You Conversions walks through that in detail.
Combine gzip with server-side caching and you've stacked two of the most impactful server optimizations available. A cached, compressed response is about as fast as your server can possibly respond. For a broader view of what sits underneath a fast server, What Fast Web Hosting Actually Looks Like Under the Hood covers the full picture.
The configuration above takes minutes to apply. Reload Nginx, verify with curl, and you're done. The performance improvement sticks around permanently with no ongoing maintenance required.