gzip on; and still
nothing is compressed.
You set gzip on;, reloaded, and your CSS is still shipping uncompressed. There are two reasons, both silent, and on a proxied application you usually have both at once. We found this on our own production server after years of it being wrong.
Confirm it in one command
Before changing anything, prove it. Ask for compression explicitly and look at the response headers:
curl -sI -H 'Accept-Encoding: gzip' https://example.com/assets/site.css \
| grep -i content-encoding
If that prints content-encoding: gzip, you are fine. If it prints nothing, the file is going
out raw. Do the same for your HTML — very often HTML is compressed and everything else is not, which is the
tell for reason one.
Reason 1 — gzip on; only covers text/html
This surprises almost everyone. gzip on; enables compression, but the default value of
gzip_types is text/html and nothing else. HTML is always
compressed and cannot be removed from the list; CSS, JavaScript, JSON, SVG and XML are not included
unless you say so.
So a config containing only gzip on; compresses your pages and none of your assets — which
is exactly the pattern that makes people think gzip is working when it mostly is not.
The trap that hid it from us
Our nginx.conf shipped with the directives present but commented out —
the Debian/Ubuntu default. A guarded edit that checked grep -q "gzip_types" matched the
comment, concluded the setting already existed, and silently did nothing. If you script this,
match on the uncommented form.
Reason 2 — gzip_proxied defaults to off
The second reason only bites if nginx is a reverse proxy — which it is for most Node, Python, Ruby, PHP-FPM and containerised applications.
gzip_proxied controls whether nginx compresses responses that came from an upstream.
Its default is off, meaning proxied responses are never compressed regardless
of what gzip_types says. Add the types but not this, and your app's responses stay raw while
static files on disk compress fine — a genuinely confusing half-working state.
The configuration
In the http block:
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_min_length 256;
gzip_types text/plain text/css
application/json application/manifest+json
application/javascript text/javascript
text/xml application/xml application/xml+rss
image/svg+xml;
- gzip_varyAdds
Vary: Accept-Encodingso caches and CDNs keep compressed and uncompressed copies apart. Without it a proxy can serve a gzipped body to a client that did not ask for one. - gzip_proxied anyCompress upstream responses too. This is the one people miss.
- gzip_comp_level 6The sensible middle. Level 9 costs noticeably more CPU for a percent or two of size.
- gzip_min_length 256Below a few hundred bytes, compression can make a response larger and always costs CPU.
- No image/video typesJPEG, PNG, WebP and MP4 are already compressed. Adding them burns CPU for nothing.
Then, and this matters: validate before reloading.
nginx -t && nginx -s reload
Keep a timestamped copy of the file first. On a box with more than one site on it, a bad reload takes every tenant down, not just the one you were working on.
What it was actually worth
Measured on this site, before and after, over the wire:
| Asset | Raw | Gzipped | Saved |
|---|---|---|---|
| Home page | 283 KB | 147 KB | 48% |
| Main stylesheet | 8.5 KB | 2.7 KB | 68% |
| Application JS | 22.6 KB | 8.3 KB | 63% |
| sitemap.xml | 2.6 KB | 0.45 KB | 82% |
| Total sampled | 338 KB | 166 KB | 50% |
Half the bytes, from four lines of configuration that were sitting commented out. It is the highest ratio of benefit to effort available on most servers, and it is invisible until you go looking — which is why it survived on ours for years.
The related win, if you are already in there: check whether your stylesheet is carrying base64 fonts.
Ours was 138 KB of which 94% was font data, all of it render-blocking. Extracting them to real
.woff2 files with font-display: swap took the render-blocking payload to
2.6 KB.
We do this properly, on infrastructure you own
Server configuration, TLS, caching, compression and the measurement to prove any of it helped. Send us a URL and we will tell you what it is currently shipping.
Book a call →