Nginx proxy_cache 能显著降低私有镜像源站压力,但需稳定proxy_cache_key、忽略上游Cache-Control、缓存404并启用proxy_cache_lock,否则默认配置必然失效。

直接结论:Nginx proxy_cache 能显著降低私有镜像源站压力,但默认配置几乎必然失效——关键不是开没开缓存,而是proxy_cache_key是否稳定、Cache-Control是否被忽略、404是否被兜底。
为什么 Composer 请求在 Nginx 里缓存命中率极低
Composer 的请求 URL 带大量动态参数:packages.json?_t=1712345678、p2/provider-laravel~10.0.json?h=abc123。Nginx 默认的 proxy_cache_key 不包含 query string,但即使开启 include $args,每次时间戳不同也会导致 key 变化,缓存形同虚设。
- packagist.org 和主流镜像源返回的响应头普遍含
Cache-Control: no-cache或max-age=0,Nginx 默认尊重该头,拒绝缓存 -
proxy_cache_valid若未显式覆盖 200/302/404 状态码,Nginx 对 404 默认不缓存,导致“包不存在”探测反复打到源站 - 未启用
proxy_cache_lock时,高并发下多个相同请求会同时回源,放大源站负载
必须重写 proxy_cache_key 并剥离干扰参数
不能依赖默认 key,必须手动构造可预测、稳定的缓存键,核心是剥离所有带时间戳或哈希的 query 参数(如 _t、h、cache-buster)。
- 用
map指令预处理$args,例如:map $args $clean_args { default ""; "~*_t=[^&]*" ""; "~*h=[^&]*" ""; "~*cache-buster=[^&]*" ""; } - 在
location块中设置:proxy_cache_key "$scheme$request_method$host$uri$clean_args"; - 避免用
$request_uri——它包含原始 query string,无法去噪 - 对
/packages.json这类高频主索引,建议单独设更短的 TTL(如proxy_cache_valid 200 1h),防止新包发现延迟
强制忽略上游 Cache-Control 并统一有效期
源站返回的 Cache-Control 是干扰项,不是指导原则。Nginx 必须主动屏蔽并接管缓存策略。
- 加这行:
proxy_ignore_headers Cache-Control Expires Set-Cookie; - 明确设定有效状态码和过期时间:
proxy_cache_valid 200 302 24h; proxy_cache_valid 404 10m; - 不要漏掉
404——私有镜像中大量试探性请求(如旧版本、已删包)会触发 404,缓存 10 分钟能极大减少无效探测 - 若源站返回
Set-Cookie(如鉴权跳转场景),也需忽略,否则 Nginx 拒绝缓存
proxy_cache_lock 在并发安装中的真实作用与陷阱
开启后,首个未命中的请求会锁住 cache key,后续相同请求排队等待而非并发回源——这对 CI 中多容器执行 composer install 极其关键。
- 必须配合前面的 clean key 使用,否则
?_t=123和?_t=456被视为两个 key,锁失效 - 调大
proxy_cache_lock_timeout(如设为30s),避免源站响应慢时锁超时,导致后续请求仍回源 - 禁用
proxy_cache_background_update——它会在缓存过期前静默刷新,与 lock 冲突,可能引发重复回源 - 注意:lock 只对完全相同的 key 生效,不跨 location,所以
/p/和/packages.json需分别配置
最易被忽略的点是:缓存策略必须和元数据访问模式匹配——/p2/ 下的 provider JSON 文件更新频率低、体积小、请求密集,适合长缓存;而 /packages.json 更新频繁,缓存太长会导致新包不可见。别用一个 proxy_cache_valid 全局套到底。


















