Nginx

Nginx Gzip Compression Not Working

When Nginx's gzip compression doesn't seem to apply — response sizes stay the same, and the expected Content-Encoding: gzip header is missing — it's usually because gzip isn't actually enabled for the specific content type being served, or a size threshold is preventing it from kicking in for smaller responses.

The Problem

You've enabled gzip in your Nginx config, but checking response headers shows no compression is actually being applied:

curl -I -H "Accept-Encoding: gzip" https://your-domain.com/app.js

The response is missing Content-Encoding: gzip, and the transferred size matches the uncompressed file size exactly.

Why It Happens

Gzip in Nginx requires several settings to align correctly, and missing any one of them results in no compression, without necessarily any error or warning. Common causes:

  • Gzip is enabled, but gzip_types doesn't include the specific MIME type of the file being served — by default, Nginx only compresses text/html, and other types (JavaScript, CSS, JSON) need to be explicitly added
  • The response is smaller than gzip_min_length, which defaults to a very small size but can be configured higher, skipping compression for responses not considered worth the CPU overhead
  • The response is being proxied from an upstream application server that's already setting its own Content-Encoding header, or Nginx's compression settings aren't correctly configured to apply to proxied responses specifically
  • The file is already served with a Content-Encoding header from a previous compression step (like a pre-gzipped static file), and Nginx doesn't double-compress an already-encoded response

The Fix

Confirm gzip is enabled and includes the MIME types you actually need compressed, not just the default HTML:

gzip on;
gzip_types text/plain text/css application/json application/javascript
           text/xml application/xml application/xml+rss text/javascript;

This list needs to explicitly include every content type you want compressed — a very common mistake is enabling gzip generally but not adding application/javascript or text/css, leaving exactly the file types that benefit most from compression uncompressed.

Check and adjust the minimum length threshold if small files aren't being compressed as expected:

gzip_min_length 256;

This value is in bytes — responses smaller than this threshold skip compression, since the overhead of compressing a very small response isn't worth the CPU cost relative to the minimal size savings.

If you're proxying to an upstream application (Node.js, Python, etc.), confirm the upstream itself isn't already setting a conflicting Content-Encoding header, and that Nginx's gzip_proxied directive is configured to actually compress proxied responses:

gzip_proxied any;

By default, gzip_proxied is quite restrictive about which proxied responses it will compress, specifically to avoid double-compressing or interfering with upstream-set headers — setting it to any tells Nginx to compress proxied responses regardless of the specific proxy-related response headers present, which is usually the desired behavior for a typical reverse proxy setup.

Verify the change actually took effect by testing directly with a client explicitly requesting gzip:

curl -H "Accept-Encoding: gzip" -I https://your-domain.com/app.js

Look specifically for Content-Encoding: gzip in the response headers, and compare the reported Content-Length against the actual uncompressed file size to confirm real compression is happening, not just the header being present without an actual size reduction.

Still Not Working?

If gzip appears correctly configured but still isn't applying, check whether a CDN or another proxy layer in front of Nginx is stripping the Accept-Encoding request header before it reaches Nginx, or stripping the Content-Encoding response header on the way back to the client — in either case, Nginx itself may be compressing correctly, but something further along the chain is interfering with the compressed response actually reaching the browser. Testing directly against Nginx's own IP or internal address, bypassing any CDN, isolates whether Nginx's configuration is genuinely the problem or something happening further downstream.