Text files such as HTML, CSS, JavaScript and JSON shrink a lot when compressed, often to a quarter of their size or less. Smaller responses load faster, use less bandwidth and score better in page speed tests. Nginx can gzip responses on the fly with a few lines of config. This guide turns it on, explains each setting, and shows how to prove it works.
Before you start
- A Linux VPS with Nginx installed (Ubuntu 22.04/24.04, Debian 12, AlmaLinux or Rocky Linux 8/9).
- SSH access with sudo.
- A site that serves text content. Images and videos are already compressed and gain nothing from gzip.
Check what is already on
Most distro packages ship with gzip on; in /etc/nginx/nginx.conf, but only for text/html. That means CSS, JavaScript and JSON are sent uncompressed. Look at the current settings:
grep -n "gzip" /etc/nginx/nginx.confOn Ubuntu and Debian you will usually see gzip on; followed by several commented-out lines starting with # gzip_.
Add a gzip config file
Rather than editing nginx.conf, put the settings in their own file. Files in /etc/nginx/conf.d/ are loaded inside the http block on every distribution covered here.
First, comment out the existing gzip on; line in nginx.conf so it is not set twice. Nginx refuses to start if the same directive appears twice in one context:
sudo sed -i 's/^\(\s*\)gzip on;/\1# gzip on;/' /etc/nginx/nginx.confThen create the new file:
sudo nano /etc/nginx/conf.d/gzip.confPaste:
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_min_length 256;
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/json
application/xml
application/rss+xml
application/atom+xml
application/ld+json
application/manifest+json
image/svg+xml
font/ttf
font/otf
application/vnd.ms-fontobject;What each setting does
gzip onswitches compression on.gzip_vary onaddsVary: Accept-Encoding, which tells caches and CDNs to store compressed and uncompressed copies separately.gzip_proxied anyalso compresses responses to requests that came through a proxy or CDN. Without it, Nginx skips compression when it sees aViaheader.gzip_comp_level 5sets the effort from 1 (fastest) to 9 (smallest). Levels above 5 or 6 cost noticeably more CPU for very small extra savings.gzip_min_length 256skips tiny responses, where the gzip header would cancel out the savings.gzip_typeslists the MIME types to compress.text/htmlis always included, so you do not list it.
Skip files that are already compressed
Do not add these to gzip_types:
- Images:
image/jpeg,image/png,image/webp,image/avif,image/gif. - Modern fonts:
font/woffandfont/woff2are already compressed formats. - Archives and media: zip, gz, mp4, mp3, pdf.
Compressing them again burns CPU and can make files slightly bigger.
Serve pre-compressed files (optional)
If your build tool already creates .gz copies of assets, for example app.js.gz next to app.js, Nginx can send those directly without compressing on every request. Add this inside the site's server block or the location for static assets:
location ~* \.(css|js|svg)$ {
gzip_static on;
}gzip_static is included in the standard packages on Ubuntu, Debian, AlmaLinux and Rocky Linux. Check with nginx -V 2>&1 | grep -o with-http_gzip_static_module.
Apply the change
sudo nginx -t && sudo systemctl reload nginxIf the test fails with "gzip" directive is duplicate, there is still a gzip on; elsewhere. Find it with sudo grep -rn "gzip on" /etc/nginx/.
Check it works
Ask for a CSS or JS file and tell the server you accept gzip:
curl -sI -H "Accept-Encoding: gzip" https://example.com/style.css | grep -iE "content-encoding|vary|content-type"You should see:
content-type: text/css
vary: Accept-Encoding
content-encoding: gzipTo see the actual saving, compare the downloaded sizes:
curl -s -o /dev/null -w '%{size_download}\n' https://example.com/
curl -s -o /dev/null -w '%{size_download}\n' -H "Accept-Encoding: gzip" https://example.com/The second number should be much smaller. In a browser, open developer tools, go to the Network tab, click a file and look for content-encoding: gzip in the response headers.
Note: If the site is behind Cloudflare or another CDN, the header you see may come from the CDN, which can recompress with its own settings. Test the origin directly with curl --resolve example.com:443:203.0.113.10... to see what Nginx sends.
Troubleshooting
- No
content-encodingheader: the file's MIME type is not ingzip_types, or the response is smaller thangzip_min_length. Check thecontent-typeheader and add it. - JavaScript not compressed: some systems label JS as
application/javascript, others astext/javascript. Keep both in the list. - Proxied app responses not compressed: add
gzip_proxied any;, and check the backend is not already sending its ownContent-Encoding. - High CPU after enabling: lower
gzip_comp_levelto 4 or 5, and make sure images are not ingzip_types. - Nginx will not reload: a duplicate
gzipdirective. Remove one copy, then runnginx -tagain.
Next steps
- Go further with a newer algorithm: Enable Brotli compression in Nginx.
- Let browsers reuse files: Set browser caching headers in Nginx.
- Faster storage and more CPU help too.
