> ## Documentation Index
> Fetch the complete documentation index at: https://notes.kodekloud.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Demo Compression

> Demonstrating enabling and verifying gzip compression in Nginx, measuring performance improvements, configuring gzip directives, and validating compressed responses in browser and server logs

In this lesson you'll learn how to enable and verify HTTP compression (gzip) in Nginx. We'll first measure performance without compression, then enable `gzip` in the Nginx configuration and confirm responses are compressed and transferred much faster. Note that compressing already-compressed image formats (JPEG, PNG, GIF) usually yields little to no benefit — this demo intentionally inflates JPEG sizes so the difference is easy to observe in browser developer tools.

We begin by testing the site with no compression and hitting backend Apache servers. Most text-based assets (HTML, CSS, JS) are already efficient, but artificially large `image/jpeg` files will exhibit long transfer times when not compressed.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/5f0mE-FaFIAKk82W/images/Nginx-For-Beginners/Performance/Demo-Compression/browser-css-nginx-no-compression-apache.jpg?fit=max&auto=format&n=5f0mE-FaFIAKk82W&q=85&s=0c2f1f043face6f3a8c3d242b77416c1" alt="A simple network diagram showing a browser requesting a CSS resource through an NGINX reverse proxy labeled &#x22;No Compression&#x22; to backend Apache web servers. Arrows indicate NGINX forwards the request to two Apache web servers." width="1920" height="1080" data-path="images/Nginx-For-Beginners/Performance/Demo-Compression/browser-css-nginx-no-compression-apache.jpg" />
</Frame>

To highlight the effect, we inflate JPEG sizes on the Apache servers, observe the slow transfers in the browser, then enable gzip in Nginx and confirm dramatic improvements.

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/5f0mE-FaFIAKk82W/images/Nginx-For-Beginners/Performance/Demo-Compression/browser-nginx-proxy-compression-apache-servers.jpg?fit=max&auto=format&n=5f0mE-FaFIAKk82W&q=85&s=11299f46dc126173d83e00c374b10207" alt="A simple architecture diagram showing a browser requesting assets (CSS, JS, HTML, JPG, XML) routed through an NGINX reverse proxy with compression. The proxy forwards the requests to multiple Apache web servers." width="1920" height="1080" data-path="images/Nginx-For-Beginners/Performance/Demo-Compression/browser-nginx-proxy-compression-apache-servers.jpg" />
</Frame>

## Preparing the environment

* Confirm your Nginx reverse proxy forwards requests to Apache upstreams. Example `upstream` and HTTP-to-HTTPS redirect:

```nginx theme={null}
# Upstream configuration
upstream example {
    server node01:443;
    server node02:443;
}

# Default HTTP -> HTTPS redirect
server {
    listen 80;
    server_name example.com;
    return 301 https://$host$request_uri;
}
```

* Example HTTPS server block on the reverse proxy (shortened for clarity):

```nginx theme={null}
server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate /etc/ssl/certs/example.com.pem;
    ssl_certificate_key /etc/ssl/certs/example.com-key.pem;

    root /var/www/html;

    add_header Strict-Transport-Security "max-age=31560000; includeSubDomains; preload";
    add_header X-Frame-Options "SAMEORIGIN";
    add_header Content-Security-Policy "default-src 'self'";
    add_header Referrer-Policy origin;

    index index.html index.htm index.nginx-debian.html;

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass https://example;
        proxy_ssl_server_name on;
    }

    location /admin {
        auth_basic "Restricted Access";
        auth_basic_user_file /etc/nginx/conf.d/.htpasswd;
    }
}
```

<Callout icon="lightbulb" color="#1CB2FE">
  Best practice: configure `proxy_set_header` lines so your backend sees the original Host and client IPs, and include `proxy_ssl_server_name on;` when proxying to HTTPS backends.
</Callout>

## Inflating images for the demo

On the Apache webserver(s) we intentionally inflate the JPEG files to simulate very large images. Run these commands in the images directory:

```bash theme={null}
cd /var/www/html/images
ll
for file in *.jpg; do fallocate -l 20M "$file"; done
```

Example listing prior to inflation:

```text theme={null}
total 136
drwxr-xr-x 2 root root 4096 Feb 17 20:31 ./
drwxr-xr-x 5 root root 4096 Feb 17 20:31 ../
-rw-r--r-- 1 root root 1259 Feb 17 20:31 logo.svg
-rw-r--r-- 1 root root 6311 Feb 17 20:31 pic01.jpg
...
-rw-r--r-- 1 root root 17129 Feb 17 20:31 pic13.jpg
...
```

After `fallocate -l 20M`, each `.jpg` reports a much larger size. This produces invalid image contents in many cases — acceptable here because we only demonstrate transfer size and compression behavior, not image fidelity.

<Callout icon="warning" color="#FF6B6B">
  Using `fallocate` as shown will change file contents and can corrupt images. Do this only in test/demo environments where file integrity doesn't matter.
</Callout>

## Monitoring access logs

Tail the Apache access log while exercising the site so you can observe incoming GET requests and response sizes:

```bash theme={null}
tail -f /var/log/apache2/access.log
```

Example access log entry:

```text theme={null}
example.com:443 127.0.0.1 - - [17/Feb/2025:20:32:46 +0000] "GET / HTTP/1.1" 200 11242 "-" "curl/7.81.0"
```

## Testing in the browser (no compression)

* Open an Incognito/private window, open Developer Tools → Network tab, and load the site.
* Without compression you'll see large transferred sizes for the inflated JPEGs and long transfer times (multiple seconds per file).

Example response headers for an uncompressed image (notice `Content-Length` \~ 20 MB and no `Content-Encoding`):

```text theme={null}
Response Headers
Content-Length: 20971520
Content-Type: image/jpeg
Date: Mon, 17 Feb 2025 20:56:45 GMT
Etag: "1400000-62e5cbe7aae75"
Last-Modified: Mon, 17 Feb 2025 20:55:27 GMT
...
```

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/5f0mE-FaFIAKk82W/images/Nginx-For-Beginners/Performance/Demo-Compression/phantom-responsive-site-network-panel.jpg?fit=max&auto=format&n=5f0mE-FaFIAKk82W&q=85&s=6601f7204b292133e7ea15234d8f0cac" alt="A browser screenshot showing a webpage header that reads &#x22;This is Phantom, a free, fully responsive site.&#x22; The developer tools Network panel is open below, listing many GET requests, file names, types and transfer sizes." width="1920" height="1080" data-path="images/Nginx-For-Beginners/Performance/Demo-Compression/phantom-responsive-site-network-panel.jpg" />
</Frame>

Firefox displays both the resource size and the transferred bytes. When uncompressed, they match and transfer times are long for each large image.

## Enabling gzip in Nginx

Edit the main Nginx configuration (typically `/etc/nginx/nginx.conf`) and add gzip settings inside the `http` block. The key directives to enable and control gzip:

* `gzip on;` — enables gzip compression.
* `gzip_vary on;` — adds `Vary: Accept-Encoding` to responses (important for caches).
* `gzip_proxied any;` — allow compression when requests come via a proxy.
* `gzip_comp_level 6;` — compression level (1–9).
* `gzip_http_version 1.1;` — ensures proper handling for HTTP/1.1 clients.
* `gzip_types` — list of MIME types to compress (text-based types are priority).

Here is a concise gzip snippet to include under the `http` block:

```nginx theme={null}
##
# Gzip Settings
##

gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript image/jpeg image/jpg font/otf font/eot font/ttf font/woff;
```

### What these gzip directives do

| Directive                | Purpose                                                                                             |
| ------------------------ | --------------------------------------------------------------------------------------------------- |
| `gzip on;`               | Enable gzip compression globally in the `http` context.                                             |
| `gzip_vary on;`          | Adds `Vary: Accept-Encoding` header so caches treat compressed/uncompressed responses separately.   |
| `gzip_proxied any;`      | Enables compression for responses served through proxies (useful for reverse-proxy setups).         |
| `gzip_comp_level 6;`     | Balances compression ratio and CPU cost (range: `1`–`9`).                                           |
| `gzip_buffers 16 8k;`    | Controls memory buffers for compression output.                                                     |
| `gzip_http_version 1.1;` | Ensures gzip is used only for HTTP/1.1+ clients where appropriate.                                  |
| `gzip_types ...`         | MIME types to compress; include text, JSON, JS, CSS, XML. Note: most images are already compressed. |

Notes:

* Keep `gzip_types` focused on compressible, text-based content (HTML/CSS/JS/JSON/XML).
* Adding `image/jpeg` to `gzip_types` generally has little benefit because JPEGs are already compressed; in this demo we included it to illustrate the effect on inflated files.

## Validate and reload Nginx

Always test the config before restarting:

```bash theme={null}
nginx -t
sudo systemctl restart nginx
```

If `nginx -t` reports errors, fix them before reloading.

## Verifying compression in the browser

Reload the page in an incognito/private window and watch the Network tab. Compressed responses will include `Content-Encoding: gzip` and `Vary: Accept-Encoding` headers. The browser shows a smaller "Transferred" size than the resource "Size" when compression is applied.

Example compressed response headers:

```text theme={null}
Response Headers
Content-Encoding: gzip
Content-Type: image/jpeg
Date: Mon, 17 Feb 2025 21:03:05 GMT
Etag: W/"1400000-62e5cbe7acdb5"
Last-Modified: Mon, 17 Feb 2025 20:55:27 GMT
Vary: Accept-Encoding
```

You will also see compressed JavaScript/CSS responses with smaller transferred sizes:

```text theme={null}
GET /assets/js/skel.min.js
Status 200
Transferred 3.76 kB (9.09 kB size)
Response Headers:
content-encoding: gzip
content-type: text/javascript
```

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/5f0mE-FaFIAKk82W/images/Nginx-For-Beginners/Performance/Demo-Compression/phantom-template-network-devtools.jpg?fit=max&auto=format&n=5f0mE-FaFIAKk82W&q=85&s=f55fc93d7c46b337a58c249c86cd2739" alt="A browser window showing the &#x22;Phantom&#x22; website template with a large headline and placeholder text. The browser's developer tools (Network tab) are open at the bottom, listing many GET requests and resource details." width="1920" height="1080" data-path="images/Nginx-For-Beginners/Performance/Demo-Compression/phantom-template-network-devtools.jpg" />
</Frame>

## Consolidated configuration examples

Below is a compact snapshot of common server-wide settings you can place in `nginx.conf` or your site-specific configuration. Adjust values for your environment.

```nginx theme={null}
events {
    worker_connections 768;
}

http {
    sendfile on;
    tcp_nopush on;
    types_hash_max_size 2048;

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

    # SSL Settings
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers on;

    access_log /var/log/nginx/access.log;
    error_log /var/log/nginx/error.log;

    # Gzip settings (as shown earlier)
    gzip on;
    gzip_vary on;
    gzip_proxied any;
    gzip_comp_level 6;
    gzip_buffers 16 8k;
    gzip_http_version 1.1;
    gzip_types text/plain text/css application/json application/javascript text_xml application/xml application/xml+rss text/javascript image/jpeg image/jpg font/otf font/eot font/ttf font/woff;

    # Rate limiting
    limit_req_zone $binary_remote_addr zone=req_limit_per_ip:10m rate=1000r/m;
    limit_req_status 429;

    # Proxy cache
    proxy_cache_path /var/lib/nginx/cache levels=1:2 keys_zone=app_cache:10m;
    proxy_cache_key "$scheme$request_method$host$request_uri";
    proxy_cache_valid 200 302 10m;
    proxy_cache_valid 404 1m;

    include /etc/nginx/conf.d/*.conf;
    include /etc/nginx/sites-enabled/*;
}
```

And a compact server block recap with headers and proxy settings:

```nginx theme={null}
server {
    listen 443 ssl;
    server_name example.com;

    ssl_certificate /etc/ssl/certs/example.com.pem;
    ssl_certificate_key /etc/ssl/certs/example.com-key.pem;

    root /var/www/html;

    add_header Strict-Transport-Security "max-age=31560000; includeSubDomains; preload";
    add_header X-Frame-Options "SAMEORIGIN";
    add_header Content-Security-Policy "default-src 'self'";
    add_header Referrer-Policy origin;

    index index.html index.htm index.nginx-debian.html;

    location / {
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_pass https://example;
    }

    location /admin {
        auth_basic "Restricted Access";
        auth_basic_user_file /etc/nginx/conf.d/.htpasswd;
    }
}
```

## HTTP headers reference

For comprehensive information about HTTP headers and their semantics, see:

* [MDN Web Docs — HTTP headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers)
* [NGINX gzip module documentation](https://nginx.org/en/docs/http/ngx_http_gzip_module.html)

<Frame>
  <img src="https://mintcdn.com/kodekloud-c4ac6d9a/5f0mE-FaFIAKk82W/images/Nginx-For-Beginners/Performance/Demo-Compression/mdn-http-headers-screenshot.jpg?fit=max&auto=format&n=5f0mE-FaFIAKk82W&q=85&s=8c7f22701457b50dd811ecc166bcf3a8" alt="A screenshot of the MDN Web Docs page titled &#x22;HTTP headers,&#x22; showing explanatory text and section links about different types of HTTP headers. The page layout includes a left navigation column, main article content in the center, and a right-hand table of contents." width="1920" height="1080" data-path="images/Nginx-For-Beginners/Performance/Demo-Compression/mdn-http-headers-screenshot.jpg" />
</Frame>

## Conclusion

* Enabling `gzip` in Nginx with a sensible `gzip_types` list and `gzip_vary on;` dramatically improves transfer times for compressible content (HTML, CSS, JavaScript, JSON, XML).
* Most modern image formats (JPEG, PNG, WEBP) are already compressed; gains from gzipping them are usually minimal. This demo inflated JPEGs to make the compression effect visible.
* Always validate configuration changes with `nginx -t` before reloading, and verify behavior using multiple clients (Chrome, Firefox, `curl`) and your browser developer tools.

Additional resources:

* NGINX gzip module: [https://nginx.org/en/docs/http/ngx\_http\_gzip\_module.html](https://nginx.org/en/docs/http/ngx_http_gzip_module.html)
* MDN — HTTP headers: [https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers)

<CardGroup>
  <Card title="Watch Video" icon="video" cta="Learn more" href="https://learn.kodekloud.com/user/courses/nginx-for-beginners/module/4a5db5c4-df5f-4291-84f0-013d1c4ce235/lesson/ab86ef5a-e11e-439b-9dbe-6aa962facf7b" />

  <Card title="Practice Lab" icon="flask-conical" cta="Learn more" href="https://learn.kodekloud.com/user/courses/nginx-for-beginners/module/4a5db5c4-df5f-4291-84f0-013d1c4ce235/lesson/a5e800f9-add5-4f9d-9970-e3d9dfed2b9e" />
</CardGroup>
