Websites & apps

Enable gzip compression in Nginx

Turn on gzip in Nginx so HTML, CSS, JavaScript and JSON are sent compressed, choose sensible types and levels, and confirm it works with curl.

BeginnerLinux server4 min read

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:

Bash
grep -n "gzip" /etc/nginx/nginx.conf

On 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:

Bash
sudo sed -i 's/^\(\s*\)gzip on;/\1# gzip on;/' /etc/nginx/nginx.conf

Then create the new file:

Bash
sudo nano /etc/nginx/conf.d/gzip.conf

Paste:

Nginx
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 on switches compression on.
  • gzip_vary on adds Vary: Accept-Encoding, which tells caches and CDNs to store compressed and uncompressed copies separately.
  • gzip_proxied any also compresses responses to requests that came through a proxy or CDN. Without it, Nginx skips compression when it sees a Via header.
  • gzip_comp_level 5 sets 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 256 skips tiny responses, where the gzip header would cancel out the savings.
  • gzip_types lists the MIME types to compress. text/html is 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/woff and font/woff2 are 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:

Nginx
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

Bash
sudo nginx -t && sudo systemctl reload nginx

If 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:

Bash
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: gzip

To see the actual saving, compare the downloaded sizes:

Bash
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-encoding header: the file's MIME type is not in gzip_types, or the response is smaller than gzip_min_length. Check the content-type header and add it.
  • JavaScript not compressed: some systems label JS as application/javascript, others as text/javascript. Keep both in the list.
  • Proxied app responses not compressed: add gzip_proxied any;, and check the backend is not already sending its own Content-Encoding.
  • High CPU after enabling: lower gzip_comp_level to 4 or 5, and make sure images are not in gzip_types.
  • Nginx will not reload: a duplicate gzip directive. Remove one copy, then run nginx -t again.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.