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
reloadoverrestartfor 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.
| Directive | Allowed context | Default | Purpose |
|---|---|---|---|
gzip | http, server, location, if in location | off | Enables or disables gzip compression. |
gzip_buffers | http, server, location | 32 4k or 16 8k, depending on platform | Sets buffers used while compressing a response. |
gzip_comp_level | http, server, location | 1 | Sets the compression level from 1 to 9. |
gzip_disable | http, server, location | No default value | Disables gzip for matching User-Agent patterns. |
gzip_http_version | http, server, location | 1.1 | Sets the minimum HTTP request version eligible for compression. |
gzip_min_length | http, server, location | 20 bytes | Skips compression until the response Content-Length meets the threshold. |
gzip_proxied | http, server, location | off | Controls compression for requests Nginx identifies as proxied through the Via request header. |
gzip_types | http, server, location | text/html | Adds MIME types beyond text/html, which is always eligible when gzip is on. |
gzip_vary | http, server, location | off | Adds 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.
| Context | Use When | Decision Point |
|---|---|---|
http | You 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. |
server | Only one hostname, application, or virtual host should inherit the gzip policy. | Useful when separate sites have different cache, proxy, or application behavior. |
location | Only 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 location | Only 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
4to6are common starting points because they improve size reduction without pushing CPU cost as high as levels8or9. - 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
20bytes, which allows very small responses to be compressed. - A threshold such as
256to1000bytes 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.
anyenables compression for all requests that Nginx sees as proxied.expired,no-cache,no-store, andprivatetarget responses that usually should not be cached by a shared proxy.authenables compression when the request includes anAuthorizationheader, which usually means the response is user-specific.no_last_modifiedandno_etagtarget 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.
| Feature | What It Does | Module Boundary |
|---|---|---|
gzip | Compresses eligible uncompressed responses at runtime. | Built into standard Nginx builds unless compiled with --without-http_gzip_module. |
gzip_static | Serves 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. |
gunzip | Decompresses 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. |
| Brotli | Uses 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.
Related Nginx Performance Optimizations
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.
- Enable Sendfile in Nginx to use zero-copy file transfers for static content that is not transformed by dynamic gzip.
- Enable Open File Cache in Nginx to cache file descriptors and reduce filesystem lookup overhead.
- Enable TCP Fast Open in Nginx to reduce connection setup latency where the network path supports it.
- Configure browser caching for static assets in Nginx to keep repeat visitors from downloading unchanged assets again.
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.


Formatting tips for your comment
You can use basic HTML to format your comment. Useful tags currently allowed in published comments:
<code>command</code>command<strong>bold</strong><em>italic</em><blockquote>quote</blockquote>