Linux VPS

Nginx proxy_cache and microcaching for dynamic sites

Cache responses from a backend app with Nginx proxy_cache: cache zones and keys, bypassing logged-in users, microcaching for one second, serving stale content during outages and purging.

AdvancedLinux VPS3 min read

When Nginx reverse-proxies to an application (Node.js, Python, Tomcat, or another web server), Nginx proxy_cache can store responses on disk and serve them without touching the backend. Even caching for a single second, called microcaching, can turn a traffic spike of thousands of requests into one backend request per second per URL.

Before you start

  • Nginx already working as a reverse proxy: Nginx reverse proxy setup.
  • For PHP-FPM sites use fastcgi_cache instead; it works the same way. See Nginx FastCGI cache.
  • Know which URLs are personalised (carts, accounts, dashboards). Those must never be shared.

Define a cache zone

In the http block (e.g. /etc/nginx/conf.d/cache.conf):

proxy_cache_path /var/cache/nginx/app levels=1:2 keys_zone=app:50m max_size=2g inactive=60m use_temp_path=off;
  • keys_zone=app:50m: shared memory for keys; 1 MB holds about 8,000 keys.
  • max_size: disk limit; the cache manager evicts least-used entries.
  • inactive: remove items not requested for this long, even if still valid.

Enable caching in the site

map $http_cookie $skip_cache {
    default 0;
    ~*(session|logged_in|wordpress_logged_in|cart) 1;
}

server {
    location / {
        proxy_pass http://127.0.0.1:3000;
        proxy_cache app;
        proxy_cache_key "$scheme$host$request_uri";
        proxy_cache_valid 200 301 10m;
        proxy_cache_valid 404 1m;
        proxy_cache_bypass $skip_cache;
        proxy_no_cache $skip_cache;
        proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
        proxy_cache_background_update on;
        proxy_cache_lock on;
        add_header X-Cache $upstream_cache_status always;
    }
}

Put the map in the http context. Adjust the cookie names to your app.

What each directive does

  • proxy_cache_bypass: don't serve from cache for these requests. proxy_no_cache: don't store their responses. Use both.
  • proxy_cache_use_stale: if the backend is down or slow, serve the last good copy. Visitors see a working site during a crash or deploy.
  • proxy_cache_background_update: refresh expired entries in the background while serving the stale one.
  • proxy_cache_lock: when 500 requests miss at once, only one goes to the backend; the rest wait for it. This prevents the "thundering herd".

Microcaching

For pages that change constantly but are the same for every anonymous visitor (news front pages, product listings), cache for one second:

proxy_cache_valid 200 1s;

Under a spike of 1,000 requests per second to the homepage, the backend now sees roughly one. Content is never more than a second old. Combined with proxy_cache_lock and use_stale updating, this is one of the cheapest scaling wins available.

Respecting backend headers

By default Nginx honours Cache-Control, Expires and Set-Cookie from the backend: responses with Set-Cookie or Cache-Control: private/no-store are not cached. That is a safe default. Override with proxy_ignore_headers only if you understand the consequences.

Check that caching works

curl -sI https://example.com/ | grep -i x-cache
curl -sI https://example.com/ | grep -i x-cache

The first shows MISS, the second HIT. Other values: BYPASS (skipped by your rule), EXPIRED, STALE, UPDATING.

Purge the cache

  • Clear everything: sudo rm -rf /var/cache/nginx/app/* (safe; Nginx rebuilds).
  • Purging single URLs needs the third-party ngx_cache_purge module or a short TTL so content expires on its own.

Troubleshooting

  • Always MISS: the backend sends Set-Cookie or Cache-Control: private on every response.
  • Users see someone else's account page: a personalised URL was cached. Add its cookie to $skip_cache immediately and clear the cache.
  • Permission denied writing cache: the directory must be writable by the Nginx worker user.

FAQ: Nginx proxy_cache

Is microcaching safe for e-commerce?

For anonymous catalogue pages yes; always bypass cart, checkout and account pages.

proxy_cache or a CDN?

Both. A CDN caches near visitors; proxy_cache protects the origin.

Does LiteSpeed have the same feature?

Yes, LSCache does this at server level with plugins handling purges.

Next steps

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.