Skip to main content
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

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:
You should see repeated requests for the same static assets, for example:
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:
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:
Quick reference — what these directives do: 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:

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:
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
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:
Then test and restart Apache:

Validating caching behavior

  1. Tail logs on both NGINX and Apache:
  1. 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):
  1. 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:
Example output after caching:
NGINX access log excerpt (serving cached assets):
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.
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.

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

Practice Lab