Skip to main content
In this lesson we configure disk caching in an NGINX reverse proxy that fronts two Apache backend web servers. First we’ll observe the behavior with no proxy cache (every request hits the backends), then enable NGINX caching so the reverse proxy can serve repeated static assets and reduce load on Apache.
A diagram showing several users connecting through a network cloud to an NGINX reverse proxy (labeled No-Cache), which forwards requests to backend web servers running Apache and serving HTML/CSS/JS assets. Arrows indicate the traffic flow from clients → proxy → servers.
At first, every asset request (HTML, CSS, JS, images, fonts, etc.) is proxied to the Apache backends and served by them. After enabling NGINX proxy caching, repeated requests for cacheable assets are served directly by NGINX from disk, saving backend CPU, memory, and network bandwidth.
A simple architecture diagram showing users connecting through a network cloud to a reverse-proxy/cache running NGINX. The NGINX proxy forwards requests to backend web servers (Apache).
Below is a concise, step-by-step walkthrough of what to change and how to verify caching.

1 — Inspect backend activity (no cache)

Tail the Apache access logs on a backend while you load the site from a client. This confirms that the origin receives every request before we enable proxy caching.
Example (shortened) Apache output showing images and CSS being served:
Also watch NGINX access logs on the proxy to see requests arriving there:
Open Developer Tools → Network in your browser and verify there are no proxy/Cache-Control headers from NGINX yet. Note that browser caching (Cache-Control, Expires) is different from NGINX proxy caching.
Browser caching (Cache-Control, Expires) is client-side. NGINX proxy caching sits between clients and the origin and lets many clients get responses without hitting the origin for each request.

2 — Make the demo results more visible

To make cache hits obvious, increase the size of a few static files on the backends so that repeating origin requests are large and easy to spot:
You should now see large file sizes (~20,971,520 bytes). This exaggeration helps demonstrate the savings achieved when NGINX serves cached responses.

3 — Configure NGINX disk cache (global settings)

Add a global cache path and defaults in the main NGINX config (commonly /etc/nginx/nginx.conf) — this must be outside any http, server, or location blocks:
Key points:
  • levels=1:2 splits the cache into subdirectories for filesystem performance.
  • keys_zone=app_cache:10m reserves memory to store cache keys (adjust as traffic grows).
  • proxy_cache_key composes the cache lookup key — include scheme, method, host, and URI to avoid collisions.
Create and secure the cache directory and set ownership to the NGINX worker user (commonly www-data):
Monitor disk usage and cache size. A misconfigured cache or too large TTLs can quickly consume disk space. Plan eviction policies and sizing for production.

4 — Enable proxy cache in the site/server config

Edit your site config (for example /etc/nginx/sites-available/example-https) and enable proxy_cache for the proxied location(s). Optionally add Cache-Control to responses for browser caching. Example server block (showing the relevant parts):
Be sure an upstream block exists and matches your backends and ports:
If proxying to HTTPS backends, enable SNI so NGINX sends the correct server name to the upstream:
Also ensure NGINX trusts the backend certificate chain (or use IPs, or disable verification in non-production testing). Use modern TLS settings for client <-> proxy and proxy <-> backend connections:
Test and reload NGINX after making changes:

5 — Add a log field on Apache to show upstream cache status

To make verification straightforward, have Apache log the X-Proxy-Cache header sent by NGINX. Add or update a LogFormat in /etc/apache2/apache2.conf or in your vhost:
Then test and restart Apache:
When NGINX forwards a request you will see "MISS" in the Apache logs for the first fetch; subsequent requests served from the cache will not reach Apache (they will not generate HIT entries in origin logs).

6 — Verify caching behavior

Follow these checks to confirm caching is working:
  1. Confirm the cache directory is initially empty or minimal:
  1. Trigger traffic (open the site or use curl). Initially NGINX will fetch items and create cache files. Example curl to show headers:
Look for X-Proxy-Cache: MISS on the first request when you included proxy_set_header X-Proxy-Cache $upstream_cache_status;.
  1. Inspect the NGINX cache directory — it should contain subdirectories and cached object files:
  1. Tail the NGINX access log to observe cached responses being served. Cached responses may show status 200 (full) or 206 (partial) depending on client ranged requests:
Example NGINX access log entries showing cached responses:
  1. Re-open the site or use a new browser session/Incognito. The Apache backend logs should show fewer repeated requests for large assets; NGINX will be serving them from cache. When Apache does see requests, the X-Proxy-Cache field will typically show "MISS" for the first fetch and not appear for subsequent HITs (because the origin is not contacted).
If you included proxy_set_header X-Proxy-Cache $upstream_cache_status; and Apache’s LogFormat captures it (%{X-Proxy-Cache}i), you’ll see whether requests were MISS, HIT, EXPIRED, or REVALIDATED.

DevTools verification

Open Browser Developer Tools → Network and inspect response headers and sizes. You should see large resource sizes (we made them large for the demo), but after the cache warms, repeated loads should not cause new origin hits. NGINX will serve cached responses, reducing backend load.
A browser screenshot showing the "This is Phantom" HTML5 UP template with three colored feature tiles near the top. The developer tools Network panel is open at the bottom, listing many GET requests and showing headers for a selected image.

Quick reference: cache settings

Summary & next steps

  • Without proxy caching, each client request hits Apache and the origin bears the full response cost.
  • We configured a disk cache with proxy_cache_path, set a cache key, and declared TTLs with proxy_cache_valid.
  • We enabled proxy_cache in the proxied location and forwarded $upstream_cache_status to the origin for visibility.
  • After the cache warms (first requests = MISS), subsequent requests are served by NGINX (HIT) and origins are spared repeated heavy responses.
Because the demo used inflated image sizes to make results obvious, a recommended next step is to enable on-the-fly compression (gzip/brotli) and set appropriate cache-control headers for production workloads.

Watch Video

Practice Lab