Enable Gzip Compression in Nginx

Enable gzip compression in Nginx with safe directive placement, syntax tests, response-header checks, and common gzip fixes.

Last updatedAuthorJoshua JamesRead time9 minGuide typeNginx

Gzip compression in Nginx reduces text-based HTTP responses before they travel from your server to the browser. HTML, CSS, JavaScript, JSON, XML, and SVG usually shrink enough to improve transfer time and bandwidth use, especially on pages with large templates or API payloads.

Use the built-in ngx_http_gzip_module when Nginx owns response compression at the origin. A safe workflow checks current response headers, places directives in a valid context, tests the configuration, reloads Nginx, and verifies Content-Encoding: gzip from the client side. If a CDN, load balancer, hosting panel, or reverse proxy also handles compression, test that layer separately because it may change the final headers visitors receive.

Check Whether Nginx Gzip Compression Is Enabled

Test a real text-based page or asset that is larger than your current gzip_min_length value. Replace the example URL with your own HTTP or HTTPS URL, then use curl to send Accept-Encoding: gzip, discard the body, and print the response headers:

curl -sS -H "Accept-Encoding: gzip" -o /dev/null -D - http://example.com/ | grep -iE "^(content-encoding|vary|content-type):"

The -H flag tells the server that the client accepts gzip, -D - prints headers to standard output, and grep keeps the verification output focused. A working gzip response with gzip_vary on includes these relevant headers:

Content-Type: text/html; charset=UTF-8
Vary: Accept-Encoding
Content-Encoding: gzip

Treat Content-Encoding: gzip as the compression proof. Vary: Accept-Encoding tells caches that the response can vary by client encoding support, but it can appear even when a specific response was not sent with gzip.

Enable Gzip Compression in Nginx

Back up the main Nginx configuration before editing it:

sudo cp /etc/nginx/nginx.conf /etc/nginx/nginx.conf.bak

The shared http block usually lives in /etc/nginx/nginx.conf. Distro packages may include extra files from conf.d, sites-enabled, or similar directories, but global gzip policy must still be loaded inside the active http context.

sudo nano /etc/nginx/nginx.conf

Add the gzip directives inside the http block. Nginx always treats text/html as eligible when gzip is enabled, so the gzip_types list only needs additional MIME types:

http {
    # Enable gzip compression
    gzip on;

    # Send Vary: Accept-Encoding for shared caches
    gzip_vary on;

    # Skip very small responses
    gzip_min_length 256;

    # MIME types to compress in addition to text/html
    gzip_types
        application/javascript
        application/json
        application/rss+xml
        application/xml
        image/svg+xml
        text/css
        text/javascript
        text/plain
        text/xml;

    # ... other directives
}

Test and Apply the Configuration

Test the configuration before applying it to the running service:

sudo nginx -t

A successful syntax test reports that the configuration is valid:

nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful

Reload Nginx after the syntax test passes:

sudo systemctl reload nginx

Prefer reload over restart for configuration-only changes. A reload applies the new configuration while existing connections finish normally, reducing avoidable service interruption.

Verify Gzip Compression After Reload

Repeat the header test against a text-based resource on your server:

curl -sS -H "Accept-Encoding: gzip" -o /dev/null -D - http://example.com/ | grep -iE "^(content-encoding|vary|content-type):"

The response should include Content-Encoding: gzip. If the public hostname sits behind a CDN or managed proxy, compare the public response with an origin-only test when possible so you know which layer owns final compression.

Nginx Gzip Directive Defaults and Contexts

The official Nginx gzip module reference defines the gzip directive defaults and allowed contexts. These defaults are Nginx module defaults, not necessarily the settings already present in a distro package template or hosting control panel.

DirectiveAllowed contextDefaultPurpose
gziphttp, server, location, if in locationoffEnables or disables gzip compression.
gzip_buffershttp, server, location32 4k or 16 8k, depending on platformSets buffers used while compressing a response.
gzip_comp_levelhttp, server, location1Sets the compression level from 1 to 9.
gzip_disablehttp, server, locationNo default valueDisables gzip for matching User-Agent patterns.
gzip_http_versionhttp, server, location1.1Sets the minimum HTTP request version eligible for compression.
gzip_min_lengthhttp, server, location20 bytesSkips compression until the response Content-Length meets the threshold.
gzip_proxiedhttp, server, locationoffControls compression for requests Nginx identifies as proxied through the Via request header.
gzip_typeshttp, server, locationtext/htmlAdds MIME types beyond text/html, which is always eligible when gzip is on.
gzip_varyhttp, server, locationoffAdds Vary: Accept-Encoding for cache correctness.

gzip_types adds MIME types in addition to text/html. Including text/html in the list is unnecessary because Nginx treats it as eligible whenever gzip on is active.

Choose Where to Place Gzip Directives

Place gzip settings as broadly as the task requires, then override only where a site or path needs different behavior. A lower context can replace a broader decision, so a narrow gzip off; can disable compression for one route even when the shared http block enables it.

ContextUse WhenDecision Point
httpYou want one gzip policy for every virtual host loaded by the main Nginx configuration.Best default for shared compression settings, but it affects all included sites.
serverOnly one hostname, application, or virtual host should inherit the gzip policy.Useful when separate sites have different cache, proxy, or application behavior.
locationOnly one path, asset class, API route, or application area needs different compression behavior.Keep this narrow so exceptions do not hide the global policy.
if in locationOnly gzip on; or gzip off; is needed inside an if block.Do not place gzip_min_length, gzip_types, gzip_comp_level, or similar directives there.

Compression on TLS responses that reflect secrets, tokens, or account-specific values can expose BREACH-style risk. Keep gzip focused on public or low-risk content when an application reflects sensitive values in compressed responses.

How Gzip Compression Works in Nginx

Without compression, Nginx sends response bodies to clients as stored on disk or generated by upstream applications. A 100 KB JavaScript file transfers as 100 KB over the network, consuming bandwidth and taking longer to download on slower connections.

With gzip enabled, Nginx compresses eligible response bodies before sending them. The browser receives a smaller payload, decompresses it locally, and renders the content without requiring visitor action.

The official NGINX compression guide notes that runtime compression can reduce transmitted data but also adds processing overhead. Nginx also avoids double-compressing responses that an upstream proxied server already compressed.

Gzip works best on text-based content because text contains repetitive patterns that compress efficiently. Binary formats such as images, video, audio, and archives usually gain little or no transfer-size benefit because those files already use their own compression.

Advanced Gzip Configuration Options

The basic configuration works well for many public pages and static assets. Add the extended settings when you need tighter control over CPU cost, proxied responses, legacy-client behavior, or a wider MIME type list:

http {
    # Enable gzip compression
    gzip on;

    # Compression level (1-9), with 4-6 a common balance
    gzip_comp_level 5;

    # Minimum response size to compress, in bytes
    gzip_min_length 256;

    # Compress selected proxied responses
    gzip_proxied expired no-cache no-store private auth;

    # Add Vary: Accept-Encoding for shared caches
    gzip_vary on;

    # Disable compression for old Internet Explorer gzip handling
    gzip_disable "msie6";

    # MIME types to compress in addition to text/html
    gzip_types
        application/atom+xml
        application/javascript
        application/json
        application/ld+json
        application/manifest+json
        application/rss+xml
        application/vnd.ms-fontobject
        application/x-javascript
        application/xml
        font/otf
        font/ttf
        image/svg+xml
        text/css
        text/javascript
        text/plain
        text/xml;

    # ... other directives
}

Choose Nginx Gzip Directive Values

Compression Level

gzip_comp_level controls compression intensity on a scale from 1 to 9. The default is 1, which uses the least CPU but gives the smallest size reduction.

  • Levels 4 to 6 are common starting points because they improve size reduction without pushing CPU cost as high as levels 8 or 9.
  • Higher levels can produce smaller output, but gains often taper off while CPU cost rises.
  • High-traffic origins should test compression level changes with real traffic patterns instead of assuming the smallest response is always the best choice.

Minimum Response Length

gzip_min_length sets the minimum response size, based on the Content-Length response header, required to trigger compression.

  • The default is 20 bytes, which allows very small responses to be compressed.
  • A threshold such as 256 to 1000 bytes avoids spending CPU on tiny responses with little or no bandwidth gain.
  • Very small compressed responses can become larger than the original because gzip adds its own header and framing overhead.

Proxied Requests

gzip_proxied controls compression for requests Nginx identifies as proxied through a Via request header. The default off disables compression for those proxied requests.

  • any enables compression for all requests that Nginx sees as proxied.
  • expired, no-cache, no-store, and private target responses that usually should not be cached by a shared proxy.
  • auth enables compression when the request includes an Authorization header, which usually means the response is user-specific.
  • no_last_modified and no_etag target responses that lack those cache-validation headers.

Vary Header

gzip_vary on adds Vary: Accept-Encoding so shared caches can keep compressed and uncompressed variants separate. Keep it enabled unless a specific upstream cache policy already handles response variants correctly.

User-Agent Disable Rules

gzip_disable disables compression for matching User-Agent regular expressions. The special value msie6 exists for legacy Internet Explorer gzip handling; modern browser traffic rarely needs custom disable rules.

MIME Types

gzip_types should target text-like formats your site actually serves: JavaScript, CSS, JSON, XML, SVG, manifests, feeds, and plain text. Avoid image, video, audio, and archive formats that are already compressed.

Distinguish Gzip, Gzip Static, Gunzip, and Brotli

Several Nginx compression features have similar names but solve different problems. Keep them separate so you do not add directives your build does not provide or configure a feature that does not match the response path.

FeatureWhat It DoesModule Boundary
gzipCompresses eligible uncompressed responses at runtime.Built into standard Nginx builds unless compiled with --without-http_gzip_module.
gzip_staticServes precompressed .gz files from disk when they exist.The official module is not built by default and requires --with-http_gzip_static_module in the build configuration.
gunzipDecompresses gzipped responses for clients that do not support gzip.The official module is not built by default and requires --with-http_gunzip_module in the build configuration.
BrotliUses a different compression algorithm from gzip.Brotli is a separate module or package path, not a ngx_http_gzip_module directive.

Check the compiled Nginx options before enabling optional compression features:

nginx -V 2>&1 | tr " " "\n" | grep -E -- "--with-http_gzip_static_module|--with-http_gunzip_module|--without-http_gzip_module" || true

If --without-http_gzip_module appears, the normal gzip directives are unavailable in that build. If --with-http_gzip_static_module or --with-http_gunzip_module does not appear, do not assume those optional directives are available without checking your package or build source.

Choose Nginx Gzip Content Types Carefully

Not all content benefits from gzip compression. Adding already-compressed or low-compression MIME types to gzip_types wastes worker CPU without reducing transfer size.

Already-Compressed Formats

  • JPEG, PNG, GIF, WebP, and AVIF images use their own compression algorithms.
  • Video formats such as MP4 and WebM, and audio formats such as MP3 and AAC, are already compressed.
  • Archive formats such as ZIP, GZIP, 7z, and RAR already use compression and usually cannot shrink further.
  • PDF files often contain compressed streams internally, so results depend on the specific document.

Binary Downloads

  • Application binaries, installers, firmware files, and package archives usually have low compression ratios.
  • The CPU cost often outweighs the minimal savings for binary downloads.

Stick to formats where gzip has a clear chance to help: HTML, CSS, JavaScript, JSON, XML, plain text, SVG, manifests, and feeds. For already-compressed downloads, let the file format carry its own compression instead of spending Nginx worker CPU on another gzip pass.

Troubleshoot Nginx Gzip Compression

If gzip compression is not working as expected, start with the response headers and narrow the cause before changing more directives.

Content-Encoding Header Is Missing

Confirm that the tested resource is a text type and large enough to pass gzip_min_length:

curl -sS -H "Accept-Encoding: gzip" -o /dev/null -D - http://example.com/path/to/file.js | grep -iE "^(content-type|content-length|content-encoding|vary):"

If the output shows Content-Type: application/javascript but your gzip_types list only includes text/javascript, add the missing MIME type, run sudo nginx -t, reload Nginx, and repeat the header test.

Vary Appears Without Content-Encoding

Vary: Accept-Encoding alone does not prove the response body was compressed. Retest with an explicit gzip-capable request and check for Content-Encoding: gzip:

curl -sS -H "Accept-Encoding: gzip" -o /dev/null -D - http://example.com/ | grep -iE "^(vary|content-encoding):"

If Vary appears but Content-Encoding is still missing, continue with the MIME type, response length, proxy/CDN, and override checks in this troubleshooting section.

Gzip Directive Is Not Allowed Here

If a directive is placed in an unsupported context, sudo nginx -t reports an emergency-level syntax error. Relevant output includes this stable phrase:

nginx: [emerg] "gzip_min_length" directive is not allowed here

The full error line includes the file path and line number that caused the failure. Move gzip_min_length, gzip_types, gzip_comp_level, and similar settings to an http, server, or location block, then rerun sudo nginx -t. Only gzip on; and gzip off; are valid inside if in location.

Nginx Reports Unknown Gzip Directive

A custom or minimal Nginx build may exclude the gzip module. Check whether the build configuration lists the disable flag:

nginx -V 2>&1 | grep -o -- "--without-http_gzip_module" || true

If the command prints --without-http_gzip_module, install a standard Nginx package or rebuild Nginx with the gzip module enabled. If the flag does not appear, recheck the directive spelling and context before assuming the module is missing.

Proxy or CDN Changes Gzip Headers

A CDN, load balancer, reverse proxy, or hosting control panel in front of Nginx may own final compression. Test the origin and the public hostname separately when possible, then check the proxy’s compression setting before assuming the origin configuration failed.

Conflicting Gzip Settings Override Each Other

More specific contexts can override broader settings. For example, gzip off; in one location block can disable compression there even when gzip on; is set in the shared http block. Review the merged configuration and print the owning file marker with each active gzip directive:

sudo nginx -T 2>/dev/null | awk '
    /^# configuration file / { file = $0 }
    /^[[:space:]]*gzip/ { print file; print }
'

The merged configuration shows settings loaded through included files, which is often where duplicate or narrower gzip rules hide.

Gzip compression fits into a broader Nginx performance stack, but each directive optimizes a different part of delivery. Dynamically gzipped responses are transformed before they leave Nginx, while file-transfer and connection settings address different bottlenecks.

Use these related optimizations when the next bottleneck is file I/O, connection setup, or repeat static-asset downloads rather than response-body size.

Conclusion

Nginx can now send smaller text responses with gzip while leaving binary downloads and already-compressed assets alone. Keep directive placement strict, enable gzip_vary for cache correctness, and verify the final response headers after each change. Tune file I/O, caching, or connection settings separately when traffic patterns point to those bottlenecks.

Share this guide

Help another Linux user troubleshoot faster

Share this guide with someone troubleshooting Linux systems or saving it for later.

Follow LinuxCapable

Want more LinuxCapable guides in Google?

Add LinuxCapable as a preferred source so Google can show more of our fresh Linux tutorials in Top Stories and From your sources when relevant.

Add LinuxCapable as a preferred source on Google
Search LinuxCapable

Need another guide?

Search LinuxCapable for package installs, commands, troubleshooting, and follow-up guides related to what you just read.

Found this guide useful?

Support LinuxCapable to keep tutorials free and up to date.

Buy me a coffeeBuy me a coffee
Before commenting, please review our Comments Policy.
Formatting tips for your comment

You can use basic HTML to format your comment. Useful tags currently allowed in published comments:

You type Result
<code>command</code> command
<strong>bold</strong> bold
<em>italic</em> italic
<blockquote>quote</blockquote> quote block

Got a Question or Feedback?

We read and reply to every comment - let us know how we can help or improve this guide.

Verify before posting: