概述
前面文章学习到了 Nginx 的静态缓存功能,该功能本质上是用来控制客户端的缓存,相关的指令有:
expires指令add_header指令
本章将深入到 Nginx 的 代理缓存(Proxy Cache),其本质上也是服务器缓存的一种。
代理缓存:指的是当 Nginx 作为 反向代理 时,将后端服务器返回的频繁请求内容存储在 Nginx 本地的内存或磁盘中,以便在后续请求相同内容时快速响应,而无需再次从后端服务器获取,减少对后端服务器的请求次数,提升性能与用户体验。
核心工作流程
完整的缓存生命周期包含 6 个关键步骤:
- 缓存键生成 - 请求到达后,Nginx 根据配置规则生成唯一的缓存键(默认由协议、代理主机名、请求 URI 组合而成)
- 缓存查找 - 用生成的缓存键在共享内存区域中快速检索是否存在对应缓存条目
- 缓存命中 - 找到未过期的有效缓存,直接读取本地缓存内容返回给客户端,完全不访问后端服务器
- 缓存未命中 - 无有效缓存时,将请求转发给后端服务器获取最新响应
- 缓存存储 - 收到后端响应后,将符合缓存规则的内容写入指定的缓存目录,同时更新缓存元数据
- 缓存失效 - 缓存条目超过设置的 TTL 时间,或长时间未被访问,会被自动清理,下次请求触发重新回源
术语与指令
相关术语
如下表所示:
| 术语 | 解释 | 补充说明 |
|---|---|---|
| 缓存键(Cache Key) | Nginx 代理缓存中的核心概念之一,其决定了 Nginx 如何识别和存储缓存内容。通俗来说,缓存键是一个唯一的标识符,用于区分不同的缓存条目,当 Nginx 接收到一个请求时,它会根据缓存键来判断是否已经缓存了该请求的响应。如果缓存键匹配,Nginx 会直接返回缓存的内容,而不需要再次处理请求 | 缓存键由指令 proxy_cache_key 进行控制,默认为 proxy_cache_key $scheme$proxy_host$request_uri;,变量表示的是协议(http 或 https)、代理主机名和请求 URI |
| 共享内存区域(keys_zone) | 专门分配的内存块,用于存储所有缓存键的元数据 | 1MB 内存约可存储 8000 个缓存键,避免每次查找都扫描磁盘 |
| TTL(Time To Live) | 缓存条目的有效时长,由 proxy_cache_valid 指令指定 |
不同 HTTP 状态码可设置不同过期时间 |
| inactive 超时 | 缓存条目在指定时间内未被访问,即使 TTL 未到期也会被删除 | 防止冷数据长期占用磁盘空间 |
| LRU 淘汰策略 | 当缓存总大小到达了 max_size 上限时,Nginx 自动清理 最近最少使用 的旧缓存 |
确保热数据始终留在缓存中 |
| 回源 | Nginx 主动向后端源站发起新请求、获取最新响应内容并更新本地缓存的完整过程 | 发生在缓存未命中或缓存失效时 |
| Stale 缓存 | 已过期的缓存内容。特定条件下 Nginx 仍可将其返回给客户端 | 配合 proxy_cache_use_stale 指令,提升可用性 |
| "静默刷新"(Background Update) | 缓存过期时,Nginx 先返回 Stale 内容给客户端,同时在后台异步回源更新缓存。由 proxy_cache_background_update 指令进行开关控制 |
常搭配 proxy_cache_use_stale updating、proxy_cache_valid、proxy_cache_lock 一起使用 |
相关指令
-
proxy_cache指令 - 启用对应的内存共享区域,例如proxy_cache my_zone;。通常配置在 location 上下文中 -
proxy_cache_valid指令 - 设置不同 http 状态码的缓存时间,例如proxy_cache_valid 200 302 10m;和proxy_cache_valid 404 1m;。通常配置在 location 上下文中 -
proxy_cache_use_stale指令- 在与后端服务器通信时,在哪些情况下可使用过期的缓存响应,例如proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;。通常配置在 location 上下文中 -
proxy_cache_path指令 - 代理缓存核心配置,如proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:50m inactive=1h max_size=1g use_temp_path=off;。仅能配置在 http 上下文中/var/cache/nginx- 设置缓存文件所存放的目录,需要提前创建且对应用户有读写权限levels=1:2- 设置两级的目录结构keys_zone=my_cache:50m- 定义 my_cache 这个共享内存区域并设置 50MB 的大小inactive=1h- 1 小时内未被访问的缓存会自动清理max_size=1g- 缓存的总大小上限use_temp_path=off- 缓存直接写入到目标目录而不需要临时的中转目录
-
proxy_cache_lock指令 - 是否开启缓存更新锁。当多个客户端同时请求同一个未缓存或已过期的资源时,Nginx 可以通过缓存更新锁避免重复生成缓存内容,从而减轻后端服务器的负载 -
proxy_cache_lock_timeout指令 - 设置缓存锁的超时时间 -
proxy_cache_background_update指令 - 是否开启 "静默刷新" -
proxy_cache_bypass指令 - 在请求阶段,定义在什么情况下跳过缓存查找过程,直接向后端服务器发起请求 -
proxy_no_cache指令 - 在响应阶段,定义在什么情况下不会将响应保存到缓存中 -
proxy_cache_revalidate指令 - 是否使用包含 "If-Modified-Since" 和 "If-None-Match" 的头字段,对已过期的缓存项进行重新验证 -
proxy_pass指令 - 设置后端服务器的协议(http 协议或 https 协议)和地址(IP 地址或域名),语法为proxy_pass URL; -
proxy_cache_key指令 - 控制缓存键的组合内容,默认为proxy_cache_key $scheme$proxy_host$request_uri; -
add_header指令- 添加标准的或自定义的响应头,如add_header X-Cache-Status $upstream_cache_status always;,其中$upstream_cache_status的取值有以下的值:- HIT - 缓存命中,直接返回缓存内容
- MISS - 缓存未命中,回源更新
- BYPASS - 匹配了
proxy_cache_bypass指令定义的指令参数,跳过缓存 - EXPIRED - 缓存已过期,回源更新
- UPDATING - 缓存过期但正在后台更新,当前返回 Stale
- STALE - 返回了过期的 Stale 缓存(后端异常或正在 updating)
- REVALIDATED - 搭配
proxy_cache_revalidate指令,确认缓存仍有效
-
proxy_set_header指令 - 在请求转发给后端服务器之前,对 HTTP 请求头进行添加、修改或删除,语法为proxy_set_header field value;
配置示例
Accept-Encoding 请求头告知后端不压缩)代理缓存通用配置:
Shell > mkdir -p /var/cache/nginx/
Shell > chown nginx:nginx /var/cache/nginx/ && chmod 755 /var/cache/nginx/
...
events {
...
}
http {
...
# 代理缓存核心配置
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:50m inactive=1h max_size=1g use_temp_path=off;
...
server {
listen 192.168.100.20:80;
server_name www.my-tests.com;
return 301 https://www.my-tests.com$request_uri;
}
server {
listen 192.168.100.20:443 ssl http2 backlog=8000 reuseport;
server_name www.my-tests.com;
# SSL/TLS 证书相关的配置
ssl_certificate /usr/local/nginx/ssl/fullchain.pem;
ssl_certificate_key /usr/local/nginx/ssl/my_privkey.key;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384';
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 15m;
ssl_stapling on;
ssl_stapling_verify on;
resolver 8.8.8.8 8.8.4.4 valid=300s;
resolver_timeout 5s;
# 在代理层统一设置压缩响应
gzip on;
gzip_vary on;
gzip_buffers 32 4K;
gzip_comp_level 5;
gzip_types text/plain text/css text/xml application/json application/javascript application/xml application/xhtml+xml image/svg+xml application/x-font-ttf application/x-font-opentype application/vnd.ms-fontobject font/woff;
gzip_min_length 256;
location / {
...
proxy_cache my_cache;
# 这里的指令参数使用的是 "$host" 而不是 "$proxy_host",主要是避免了重新缓存
proxy_cache_key "$scheme$host$request_uri";
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_valid 500 502 503 504 10s;
proxy_cache_lock on;
proxy_cache_lock_timeout 5s;
proxy_cache_background_update on;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
# 添加自定义的调试响应头
add_header X-Cache-Status $upstream_cache_status always;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Accept-Encoding ""; # 禁止后端压缩
# 指向的是单台后端服务器,也可以指向一组服务器,只不过我们还没有学习代理相关的内容
proxy_pass https://192.168.100.30;
}
}
}
代理缓存清理
代理缓存自动清理的依据是:
- TTL(Time To Live)
- inactive 超时
- LRU 淘汰策略
除了自动清理之外,您还可以手动清理,如:
# 清理全部缓存(生产环境中谨慎使用)
Shell > rm -rf /var/cache/nginx/*
调试与验证
# 第一次请求,预期 MISS
Shell > curl -I https://www.my-tests.com/
# 第二次请求,预期是 HIT
Shell > curl -I https://www.my-tests.com/
# 带 Cookie 请求(预期 BYPASS,如果配置了 "proxy_cache_bypass $cookie_sessionid")
Shell > curl -I -H "Cookie: sessionid=abc123" https://www.my-tests.com/
# 调试时注意响应头的内容
...
HTTP/2 200
Server: nginx
X-Cache-Status: HIT
Content-Type: text/html
...
# 查看缓存目录的结构
Shell > tree /var/cacahe/nginx/
常见问题与排错
Q:缓存始终 MISS 的原因有哪些?
大概有两种:
- 后端响应头包含
Set-Cookie- 在 Nginx 主配置文件中配置proxy_ignore_headers Set-Cookie; - 后端返回
Cache-Control: no-cache- 在 Nginx 主配置文件中配置proxy_ignore_headers Cache-Control;










