Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

iTechGuides is reader-supported. When you buy through links on our site, we may earn an affiliate commission. As an Amazon Associate I earn from qualifying purchases. Learn more

Enable NGINX gzip in the shared http block, include the active MIME registry, and explicitly list the response types you want compressed. NGINX decides eligibility from the response Content-Type, not from a filename extension. That is why a CSS or JavaScript file can remain uncompressed even when gzip on; is present: its MIME mapping may be missing or overridden, the response may be below gzip_min_length, the client may not have negotiated gzip, or a proxy policy may exclude it.

How NGINX decides whether to compress

The ngx_http_gzip_module is a response filter. With gzip on;, NGINX can compress an eligible response while sending it to a client that advertises gzip support. Two separate decisions are involved:

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
  • Is gzip enabled? The default is off.
  • Is this response type eligible? The default gzip_types value includes only text/html. Additional types must be listed explicitly.

NGINX compares gzip_types with the response MIME value in Content-Type. It does not compare the requested URL’s suffix. A URL ending in .css is compressed only when NGINX maps that file to text/css and that MIME type is listed in gzip_types. An unknown extension that falls back to application/octet-stream will not match a CSS or JavaScript entry.

The request-to-response decision

  1. The request is routed to a particular server and location block.
  2. NGINX determines the response MIME type from the active types mappings. If no mapping applies, default_type is used.
  3. The client request is checked for gzip negotiation, normally the Accept-Encoding: gzip header.
  4. NGINX checks the response length against gzip_min_length.
  5. For an upstream response, gzip_proxied can add another policy decision.
  6. If every condition permits compression, NGINX sends Content-Encoding: gzip. With gzip_vary on;, it also sends Vary: Accept-Encoding so caches distinguish compressed and uncompressed variants.

Use a correct baseline configuration

Put shared compression and MIME settings in the active http block unless a narrower scope is deliberate. The following example covers common text, data, vector, font, and WebAssembly responses:

http {
    include       /etc/nginx/mime.types;
    default_type  application/octet-stream;

    gzip on;
    gzip_vary on;
    gzip_min_length 1000;
    gzip_types
        text/plain
        text/css
        text/xml
        application/xml
        application/json
        application/javascript
        application/rss+xml
        application/atom+xml
        image/svg+xml
        font/woff
        font/woff2
        application/wasm;

    # server blocks follow here
}

gzip and gzip_types are valid in http, server, and location contexts. Keeping the directives in http gives all virtual hosts the same policy; place them lower only when a particular site or path genuinely needs different behavior. The listed types are added to NGINX’s built-in text/html entry.

What each setting controls

Directive Baseline or default Purpose
gzip off Enables runtime gzip filtering.
gzip_types text/html Adds response MIME types eligible for compression; it does not select extensions.
gzip_vary off unless enabled Adds Vary: Accept-Encoding when enabled, helping caches keep negotiated variants separate.
gzip_min_length module default if omitted Skips responses shorter than the configured threshold. The example uses 1000.
gzip_proxied no special proxy allowance unless configured Controls compression decisions for responses received from an upstream proxy.
default_type often text/plain unless set by the package configuration Supplies a MIME type when no extension mapping applies. The example deliberately uses application/octet-stream; that fallback will not match the text entries above.

Enable gzip safely, step by step

1. Find the configuration that is actually loaded

Packages commonly start with a main file such as /etc/nginx/nginx.conf and include files from a conf.d or sites-enabled directory. Do not assume that the file you edited is active. The expanded configuration command in the verification section shows every included file and is the authoritative way to check.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

2. Add the MIME include and gzip policy

Inside the active http block, ensure the MIME registry is included before the virtual-server definitions, then add the gzip directives from the baseline example. If your distribution stores the registry at a different path, use that package’s existing path rather than creating a second, conflicting registry.

3. Check for narrower overrides

A server or location block can define its own types, default_type, gzip, or gzip_types values. Such a block can make one asset directory behave differently from the rest of the site. Search the expanded configuration for the affected hostname and URI, and remove an accidental override or make the narrower policy complete.

4. Validate before reloading

sudo nginx -t

Proceed only when the command reports a successful syntax and configuration test. Then reload the service using the service manager used by your system, for example:

sudo systemctl reload nginx

A reload applies the new workers without needlessly stopping existing connections. If validation fails, fix the reported file and line first; do not reload a configuration that has not passed nginx -t.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Fix missing or incorrect MIME types

NGINX sets Content-Type from its MIME metadata. The standard registry contains mappings for common web assets, including the following:

Extension Expected Content-Type Include in gzip_types?
.css text/css Yes
.js and modern JavaScript module files application/javascript Yes
.json application/json Yes
.svg image/svg+xml Yes
.woff font/woff Only when testing shows a worthwhile benefit
.woff2 font/woff2 Only when testing shows a worthwhile benefit
.xml application/xml or text/xml, depending on the mapping List the value actually returned
.wasm application/wasm Yes

Confirm the mapping before changing it

Request the exact asset and inspect its headers. If a stylesheet returns application/octet-stream, the compression list is not the first problem: the MIME mapping is wrong or not loaded. If it returns text/css but lacks gzip, investigate negotiation, length, proxy policy, or a configuration override instead.

Add a genuinely missing mapping

The safest fix is to add the mapping to the MIME registry that your active configuration already includes, or to correct that registry’s existing entry. A mapping has the form:

types {
    application/wasm  wasm;
}

Do not place a small replacement types block in a narrow location and assume it augments every inherited mapping. A later types or default_type setting can change the result for that context. After editing, inspect the expanded configuration and test a real response. Once the response has the intended MIME value, add that exact value to gzip_types if it is not already present.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Why adding an extension to gzip_types fails

This does not work:

gzip_types css js json;

gzip_types accepts MIME values, not suffixes. The working form uses values such as text/css, application/javascript, and application/json. Avoid using gzip_types * as a blanket repair: binary files and formats that are already compressed can consume CPU without reducing the payload.

Why CSS or JavaScript is still not compressed

The client did not negotiate gzip

Compression is negotiated per request. A browser normally sends Accept-Encoding: gzip, but a diagnostic client, health check, or intermediary may not. Test with an explicit header so that the result is unambiguous:

curl -sS -D - -o /dev/null -H 'Accept-Encoding: gzip' https://example.com/assets/app.css

Look for both the expected Content-Type and Content-Encoding: gzip. Without the request header, an uncompressed response is expected and does not prove the server policy is broken.

The response is below the minimum length

NGINX can deliberately skip small responses. If the asset is shorter than gzip_min_length, lower the threshold for your workload or test a larger representative file. Do not infer behavior from a tiny development stylesheet when production bundles are much larger.

Free tools Windows power users keep installed

One-click scans. No signup required.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The MIME value does not match

Inspect the returned value character for character. A JavaScript file served as text/plain, application/octet-stream, or an unexpected vendor type will not match application/javascript. Either correct the mapping or add the actual, intentional MIME value to gzip_types; correcting the mapping is preferable because it also fixes browser behavior and caching metadata.

A location or server block overrides the shared policy

Static assets are often handled by a dedicated location. Check that location for gzip off;, its own gzip_types, a replacement types block, or a default_type that catches the files before the normal registry does. Confirm the request is reaching the intended server block by checking the hostname, listen address, and location pattern in the active configuration.

The response comes from an upstream proxy

When NGINX receives a response from an application server, gzip_proxied controls whether that response may be compressed. A policy intended for static files does not automatically permit every proxied response. Inspect the upstream-related headers and configure the directive deliberately, as shown in the next section.

The installed build lacks the gzip module

Run:

nginx -V 2>&1

The build information can reveal whether the HTTP gzip module was disabled with --without-http_gzip_module. The module requires zlib. If it is absent, configuration syntax for gzip cannot provide runtime compression; install a package or build that includes the module, then retest.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Configure compression for reverse-proxied responses

The official administration example uses:

server {
    gzip on;
    gzip_types      text/plain application/xml;
    gzip_proxied    no-cache no-store private expired auth;
    gzip_min_length 1000;
}

gzip_proxied parameters describe conditions under which a proxied response may be compressed. For example, the listed policy permits compression when cache-control indicates no-cache, no-store, or private, when the response is expired, or when authorization is involved. The correct choice depends on how your proxy and cache are designed; copying the list without checking those semantics can create inconsistent cache behavior.

For a proxied failure, capture Via, Cache-Control, Expires, Last-Modified, and ETag along with Content-Type and Content-Encoding. Those headers show whether NGINX recognized an upstream response and which cache conditions influenced the decision. If an intermediary rewrites or strips Vary, fix that cache layer as well as the NGINX setting.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Runtime gzip versus precompressed .gz files

Runtime gzip and gzip_static solve different operational problems:

Approach How it works Main cost Requirements and trade-offs
Runtime gzip gzip on; compresses eligible responses as they are sent. CPU work on request paths and potentially variable output timing. Uses the normal gzip module and zlib; simple for dynamic or changing content.
Precompressed delivery gzip_static on; looks for a matching file ending in .gz and serves it to a gzip-capable client. Build or deployment storage and generation time instead of per-request compression. Requires the optional static module and a correctly generated, deployed .gz alongside the original.

How gzip_static behaves

gzip_static does not perform on-the-fly compression. It merely uses a file compressed beforehand by a compatible tool. If the client does not support gzip, or the matching .gz file is absent or unsupported, NGINX sends the uncompressed original.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The module is not built by default. A build must include --with-http_gzip_static_module. Check with nginx -V before adding the directive. Generate files during your asset build or deployment, for example with the system gzip command, and deploy the original and compressed versions together. Keeping their modification times aligned is recommended by the module documentation so that freshness checks remain coherent.

Precompression is attractive for large, immutable bundles because output is reproducible and request-time CPU use is removed. Runtime gzip is usually simpler for responses generated at request time. In either model, the MIME type and client negotiation still have to be correct.

Verify the active configuration and negotiated headers

Inspect every included file

sudo nginx -T

This prints the fully expanded configuration, including files pulled in by include. Search it for the affected server name, the asset location, mime.types, default_type, gzip_types, and gzip_proxied. This often exposes a duplicate virtual host or later override that is invisible when looking at one file in isolation.

Test the response directly

curl -sS -D - -o /dev/null -H 'Accept-Encoding: gzip' https://example.com/assets/app.js

A successful negotiated response should show a JavaScript MIME type and Content-Encoding: gzip. When gzip_vary on; is active, it should also show Vary: Accept-Encoding. Test a CSS file, a JSON response, and an SVG or WebAssembly asset separately; one correct result does not prove every location uses the same mappings.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Compare with a request that does not advertise gzip

curl -sS -D - -o /dev/null https://example.com/assets/app.js

The second request should normally lack Content-Encoding: gzip. This confirms that NGINX is negotiating rather than blindly labeling every response as compressed. If both requests have the same result, inspect the request path, the client header, and any intermediary cache.

Performance and security boundaries

Do not spend CPU on formats that are already compressed

JPEG, PNG, MP4, ZIP, and similar binary formats generally contain their own compression. Adding them to gzip_types often increases CPU use with little or no size reduction. Keep the list focused on text and other formats for which testing demonstrates a benefit.

Account for runtime overhead

Runtime compression reduces transmitted bytes but adds processing work at request time. The useful balance depends on response size, traffic, CPU capacity, and whether the content is dynamic. Precompress immutable assets when build complexity and storage are acceptable; use runtime gzip where content cannot conveniently be generated ahead of time.

Consider BREACH exposure

The NGINX gzip documentation warns that compressed responses sent over SSL/TLS can be vulnerable to BREACH-style attacks. Treat pages that combine secrets with attacker-controlled input differently from ordinary static assets. Review whether sensitive responses should be compressed, and apply the decision consistently across the application and proxy layers.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Production checklist

  • The active http block includes the intended MIME registry.
  • gzip on; is enabled in the scope serving the asset.
  • The response’s actual Content-Type appears in gzip_types.
  • No narrower types, default_type, or gzip off; override changes that location.
  • The test request sends Accept-Encoding: gzip.
  • The response is at least as large as the configured gzip_min_length.
  • Proxy responses have a deliberate gzip_proxied policy based on their cache headers.
  • nginx -t passes before reload, and nginx -T confirms the expanded configuration.
  • nginx -V confirms the runtime gzip module; use the separate static-module check before enabling gzip_static.
  • Verification checks Content-Type, Content-Encoding, and, when enabled, Vary: Accept-Encoding.

Choosing the final setup

For most NGINX sites, start with the shared configuration shown above, correct the MIME registry, and verify real responses with curl. Use runtime gzip for dynamic output and for the simplest deployment. Use gzip_static for generated, cacheable assets when your NGINX build includes the optional module and your deployment process can keep original and .gz files synchronized. In both cases, correctness begins with the MIME type NGINX actually sends, not the extension visible in the URL.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.