r/nginx • u/dvershinin • 3h ago
Purging a Vary'd cache key makes nginx walk the whole cache zone. What that costs, and an index that fixes it
If your upstream sends Vary: Accept-Encoding (or you turn on gzip_vary), nginx stores one cache file per header value under the same key. The file name hashes the key together with the client's header value, so a purge request can't compute the variant names, and nginx keeps no index of them. The only correct way to purge all variants is to walk the whole zone and read each file's KEY: line.
What that walk costs, single worker, ext4:
- 100k files: 4.5 s cold, 0.2 s warm
- 1M files: 227 s cold, ~1 s warm
The worker serves nothing for that whole time.
The fix in ngx_cache_purge 2.7.0 is a shared-memory index of variant hashes per key. The tricky part is correctness: an index that might miss a variant leaves stale content servable after a "successful" purge. So an entry is only created right after a full-scan purge removed every file for the key, the one moment "no variants exist" is provably true. A header filter then records each new variant. A restart, eviction, or more than 64 variants drops the entry and the next purge falls back to the scan. Repeat purges land at 0.2-2 ms regardless of zone size.
Write-up with the benchmark tables and which purges still walk the zone (wildcard, purge_all, cache tags): https://www.getpagespeed.com/server-setup/nginx/nginx-vary-cache-purge-variant-index
Disclosure: I maintain the repo that packages this module.