> ## 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 Caching

> Demonstrates adding disk-backed NGINX proxy caching in front of Apache backends to reduce backend load, configure proxy_cache, enlarge assets, and verify hits via logs and browser DevTools

In this walkthrough you'll add proxy caching to an NGINX reverse proxy sitting in front of two Apache backend servers (node01 and node02). Before caching, every request for static assets (CSS, images, JS, fonts) is forwarded to one of the Apache nodes. After enabling caching, NGINX stores upstream responses on disk and serves them directly — reducing backend load and improving latency.

What you'll do

* Observe backend access logs while the proxy is uncached.
* Create larger test assets so caching effects are obvious.
* Add global NGINX cache configuration and enable the cache in the site/server block.
* Verify cache hits/misses in logs and confirm files under the cache directory.

Prerequisites / links

* NGINX proxy\_cache docs: [https://nginx.org/en/docs/http/ngx\_http\_proxy\_module.html#proxy\_cache](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache)
* Apache LogFormat / mod\_log\_config: [https://httpd.apache.org/docs/current/mod/mod\_log\_config.html](https://httpd.apache.org/docs/current/mod/mod_log_config.html)
* Chrome DevTools network panel: [https://developer.chrome.com/docs/devtools/network/](https://developer.chrome.com/docs/devtools/network/)

## Observing backend traffic (no cache)

Tail the Apache access log on node01 (and node02) and browse the site to see each request hitting a backend:

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

You should see repeated requests for the same static assets, for example:

```text theme={null}
192.231.221.4 - - [17/Feb/2025:01:20:34 +0000] "GET / HTTP/1.1" 200 1597 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) ..."
192.231.221.4 - - [17/Feb/2025:01:20:34 +0000] "GET /images/logo.svg HTTP/1.1" 200 1259 "-" "Mozilla/5.0 ..."
192.231.221.4 - - [17/Feb/2025:01:20:34 +0000] "GET /assets/css/main.css HTTP/1.1" 200 8551 "-" "Mozilla/5.0 ..."
192.231.221.4 - - [17/Feb/2025:01:20:34 +0000] "GET /images/pic04.jpg HTTP/1.1" 200 6499 "-" "Mozilla/5.0 ..."
...
```

This confirms both Apache backends are serving the same static files repeatedly.

## Step 1 — Increase asset sizes for a clearer demo

To make caching benefits (transfer time and load reduction) obvious, enlarge image files on both backends. On each Apache node:

```bash theme={null}
# On node01 (repeat on node02)
cd /var/www/html/images
for file in *.jpg; do
  fallocate -l 20M "$file"
done

# Verify
ls -lh
```

Each `.jpg` should now be \~20M. Repeatedly serving these large files from Apache will show a clear performance difference once NGINX starts serving them from cache.

## NGINX global cache configuration

Add cache-related directives to the main NGINX configuration (for example in `/etc/nginx/nginx.conf` or an included file). Key directives:

```nginx theme={null}
##
# Caching
#

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;
```

Quick reference — what these directives do:

| Directive | Purpose | Example / note |
| - | - | - |
| `proxy_cache_path` | Defines cache storage path, directory hashing levels, and named `keys_zone` (in-memory index). | `proxy_cache_path /var/lib/nginx/cache levels=1:2 keys_zone=app_cache:10m;` |
| `proxy_cache_key` | Determines how a request maps to a cache object (use variables like `$host` and `$request_uri`). | `proxy_cache_key "$scheme$request_method$host$request_uri";` |
| `proxy_cache_valid` | Sets TTLs for cached responses by status code. | `proxy_cache_valid 200 302 10m; proxy_cache_valid 404 1m;` |

Notes:

* `levels=1:2` splits the cache into nested subdirectories (improves filesystem performance with many files).
* `keys_zone=app_cache:10m` creates a 10MB shared memory zone for cache keys — adjust size for your workload.
* `proxy_cache_valid` lines define TTLs for cached responses.

Create the cache directory and set ownership so NGINX can write to it:

```bash theme={null}
sudo mkdir -p /var/lib/nginx/cache
sudo chown -R www-data:www-data /var/lib/nginx/cache
```

## Site (server) configuration: enable proxy caching

Edit your site server block (for example `/etc/nginx/sites-available/example-https`) and enable the cache for locations that proxy to the upstream. Add a request header so the backend can log whether NGINX forwarded the request. Optionally, add a response header to expose cache status to clients.

Example server configuration:

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

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=31536000; includeSubDomains; preload";
    add_header X-Frame-Options "SAMEORIGIN";
    add_header Content-Security-Policy "default-src 'self'";
    add_header Referrer-Policy origin;
    add_header Cache-Control "public, max-age=3600";

    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;

        # Forward the upstream cache status to the backend so it can log HIT/MISS
        proxy_set_header X-Proxy-Cache $upstream_cache_status;

        # Use the cache defined globally
        proxy_cache app_cache;

        proxy_pass https://example;
    }

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

Notes:

* The `proxy_set_header X-Proxy-Cache $upstream_cache_status;` request header allows backends to log `HIT`/`MISS`/`EXPIRED`. To expose cache status to clients, use `add_header X-Proxy-Cache $upstream_cache_status;` (response header).
* Avoid caching sensitive locations (e.g., `/admin`).

Test and reload NGINX

```bash theme={null}
sudo nginx -t
sudo nginx -s reload
```

## Update Apache log formats (optional, recommended)

To surface the `X-Proxy-Cache` request header in Apache access logs, add or update a LogFormat line to include `%{X-Proxy-Cache}i`, for example:

```apache theme={null}
LogFormat "%v:%p \"%{X-Forwarded-For}i\" \"%{X-Forwarded-Proto}i\" \"%{X-Proxy-Cache}i\" %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" vhost_combined
```

Then test and restart Apache:

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

## Validating caching behavior

1. Tail logs on both NGINX and Apache:

```bash theme={null}
# On the proxy
tail -f /var/log/nginx/access.log

# On node01 (Apache)
tail -f /var/log/apache2/access.log
```

2. From your browser, open the site in an Incognito/Private window to avoid browser caching artifacts. On the first requests NGINX should MISS and forward to the backend; Apache logs will include the `X-Proxy-Cache` value (example):

```text theme={null}
example.com:443 "174.0.252.84, 34.117.152.159, 169.254.169.126, 192.168.1.144, 192.231.221.4" "https" "MISS" 192.231.221.4 - - [17/Feb/2025:01:35:09 +0000] "GET /images/pic01.jpg HTTP/1.0" 200 21049497 "-" "Mozilla/5.0 ..."
```

3. On subsequent requests NGINX should serve cached responses. You will typically observe:

* No repeated entries in Apache logs for cached assets.
* NGINX access log showing cached responses (sometimes `206` Partial Content if clients request byte ranges).
* Files and subdirectories populated under `/var/lib/nginx/cache`.

Check the cache directory:

```bash theme={null}
ls -l /var/lib/nginx/cache
```

Example output after caching:

```text theme={null}
drwx------ 13 www-data www-data 4096 Feb 17 01:35 .
drwxr-xr-x  8 root     root     4096 Feb 17 01:29 ..
drwx------  3 www-data www-data 4096 Feb 17 01:35 1/
drwx------  3 www-data www-data 4096 Feb 17 01:35 2/
...
```

NGINX access log excerpt (serving cached assets):

```text theme={null}
192.231.221.4 - - [17/Feb/2025:01:38:22 +0000] "GET /images/pic12.jpg HTTP/1.1" 206 2097136 "-" "Cloud-CDN-Google (GFE/2.0)"
192.231.221.4 - - [17/Feb/2025:01:38:22 +0000] "GET /images/pic06.jpg HTTP/1.1" 206 160 "-" "Cloud-CDN-Google (GFE/2.0)"
...
```

Note: `206` responses indicate partial content (byte-range requests). The key observation is that cached assets do not produce repeated backend log entries.

## Browser DevTools confirmation

Open Developer Tools (Network tab) and refresh. You should see the large image sizes (e.g., \~21 MB) for the cached resources in the panel. NGINX will add the response headers (e.g., `Cache-Control`) you configured. Apache logs should only show `MISS` on the initial requests that populated the cache.

![A browser screenshot showing the "Phantom" HTML5 UP site template with large headline text and colorful tile cards on the left. On the right is Chrome DevTools' Network panel listing resource requests, statuses, sizes and timings.](https://mintcdn.com/kodekloud-c4ac6d9a/ij3FNIPjp1w0Myd0/images/Nginx-for-Beginners/Performance/Demo-Caching/phantom-html5up-devtools-network.jpg?fit=max\&auto=format\&n=ij3FNIPjp1w0Myd0\&q=85\&s=7e136f371ad605bba0c40c8531fe8054)

You can inspect network entries and headers to confirm the proxy is serving cached content and that backend logs only show entries when NGINX forwards requests during cache misses or refreshes.

## Second DevTools screenshot (page + network)

![A browser window showing a webpage titled "This is Phantom, a free, fully responsive site template" with three colored content cards. The lower half shows the developer tools Network panel listing many GET requests and response headers.](https://mintcdn.com/kodekloud-c4ac6d9a/ij3FNIPjp1w0Myd0/images/Nginx-for-Beginners/Performance/Demo-Caching/phantom-template-three-cards-network-panel.jpg?fit=max\&auto=format\&n=ij3FNIPjp1w0Myd0\&q=85\&s=8dce10aa44f2443a99ff7a6fd801719f)

## Summary and next steps

* You configured a disk-backed NGINX proxy cache with a memory keys zone and enabled it in your site config.
* Adding `proxy_set_header X-Proxy-Cache $upstream_cache_status;` lets backend logs show whether requests were forwarded (MISS) or not (HIT).
* Enlarging assets made the benefits of caching (reduced backend load and faster subsequent responses) obvious.

Consider these improvements:

* Enable compression (gzip, brotli) for appropriate content types before caching to reduce bandwidth and storage.
* Tune `keys_zone` and cache TTLs to match traffic patterns and content volatility.
* Use `proxy_cache_bypass`, `proxy_cache_valid`, and `proxy_cache_lock` to handle dynamic content, error handling, and thundering herd protection.

Links and references

* NGINX proxy\_cache documentation: [https://nginx.org/en/docs/http/ngx\_http\_proxy\_module.html#proxy\_cache](https://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache)
* Apache LogFormat: [https://httpd.apache.org/docs/current/mod/mod\_log\_config.html](https://httpd.apache.org/docs/current/mod/mod_log_config.html)
* Chrome DevTools Network panel: [https://developer.chrome.com/docs/devtools/network/](https://developer.chrome.com/docs/devtools/network/)
* fallocate(1) man page: [https://man7.org/linux/man-pages/man1/fallocate.1.html](https://man7.org/linux/man-pages/man1/fallocate.1.html)

Remember: browser caching (via Cache-Control) is different from NGINX proxy caching. Configure both appropriately — Cache-Control controls client/browser caching; proxy\_cache controls what NGINX stores and serves to subsequent clients.

> **[Watch Video](https://learn.kodekloud.com/user/courses/nginx-for-beginners/module/4a5db5c4-df5f-4291-84f0-013d1c4ce235/lesson/42757c8a-6e6c-48c7-b89e-a4ada8791ad9)**

> **[Practice Lab](https://learn.kodekloud.com/user/courses/nginx-for-beginners/module/4a5db5c4-df5f-4291-84f0-013d1c4ce235/lesson/1559660d-8686-4508-8474-33f2e7fbfdd4)**


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.