A cache that expires and then blocks every waiting request on one slow origin fetch is a cache that makes the worst moment worse.
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=app:64m
max_size=2g inactive=60m use_temp_path=off;
location / {
proxy_cache app;
proxy_cache_valid 200 10m;
proxy_cache_use_stale updating error timeout http_500 http_502;
proxy_cache_background_update on;
proxy_cache_lock on;
add_header X-Cache-Status $upstream_cache_status;
}
proxy_cache_lock alone removes the stampede: one request populates the entry and the rest wait for it rather than all hitting the origin. use_stale updating with background_update is better still — the waiting requests get the stale copy immediately and the refresh happens behind them. The status header is what makes any of this verifiable and is worth leaving on permanently.