When a backend is slow but its responses are the same for many users, cache them in Nginx. Declare the cache store at the http { } level, then switch it on per location.
At http { }:
proxy_cache_path /var/cache/nginx
levels=1:2
keys_zone=app_cache:10m # 10m of keys ≈ ~80k entries
max_size=1g
inactive=60m
use_temp_path=off;
In the server { }:
location / {
proxy_pass http://127.0.0.1:3000;
proxy_set_header Host $host;
proxy_cache app_cache;
proxy_cache_valid 200 301 302 10m; # cache OK/redirects for 10 min
proxy_cache_valid 404 1m;
proxy_cache_key "$scheme$request_method$host$request_uri";
# Serve stale content while refreshing or if the backend errors.
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
proxy_cache_lock on; # collapse a stampede into one upstream fetch
add_header X-Cache-Status $upstream_cache_status; # HIT / MISS / EXPIRED / STALE
}
How it behaves:
- The first request is a MISS (goes to the backend and is stored); subsequent identical requests are HIT (served from disk) until
proxy_cache_validexpires. X-Cache-Statuslets you verify withcurl -I— look forx-cache-status: HIT.proxy_cache_use_stale+background_updatekeep the site fast and resilient: users get slightly stale content instantly while Nginx refreshes in the background, and stale content is served if the backend is down.proxy_cache_lock on;prevents a thundering herd — only one request repopulates an expired entry.
Don’t cache personalised pages. Bypass the cache when a session cookie is present:
proxy_cache_bypass $cookie_sessionid;
proxy_no_cache $cookie_sessionid;
Also respect upstream Cache-Control: no-store/private — Nginx won’t cache those by default.
sudo nginx -t && sudo nginx -s reload