Nginx反向代理缓存如何配置与验证?香港服务器高并发场景实操
开启了 proxy_cache,页面却仍然每次回源;把缓存时间设为一分钟,内容却过了几分钟还没更新。这两种现象并不矛盾:Nginx是否读取、写入和继续使用缓存,取决于缓存键、请求身份、源站响应头以及过期处理策略,而不只是某一个配置项。
在香港服务器的高并发场景中,正确做法是先划定“哪些响应可以共享”,再配置缓存区域、缓存键、绕过条件和失效方式,最后通过响应头、访问日志及受控压测验证。缓存能够减少重复回源和应用计算,但不能直接消除访问链路时延,也不能替代连接数、文件描述符和带宽容量检查。
围绕香港服务器上的网站、接口与数据库业务,A5数据提供入门建站、Xeon Gold及AMD EPYC等物理服务器方案,搭配SSD或NVMe存储,并提供CN2与国际带宽选择,为Nginx缓存文件、上游应用和日志数据提供相应的计算、存储与网络资源。面向不同访问区域的业务,A5数据还覆盖美国、日本、韩国、新加坡等地区的服务器产品,便于结合部署位置与线路需求构建多地区服务架构。
一、概念边界:反向代理缓存应该放在哪一层
页面、对象、查询与边缘缓存不是同一件事
Nginx反向代理缓存存储的是上游返回的HTTP响应,通常包括响应头和响应体。它适合缓存公开页面、公共JSON接口、下载文件等可复用内容,不会自动理解数据库查询结果或业务对象的失效关系。
| 缓存层级 | 适合的内容 | 常见失效方式 | 验证重点 |
|---|---|---|---|
| Nginx反向代理缓存 | 公开页面、公共接口、可重复下载内容 | TTL、缓存键版本、定向清理 | 是否命中、是否回源、内容是否一致 |
| 应用对象缓存 | 商品对象、配置对象、聚合结果 | 更新后删除、版本号、事件通知 | 更新是否传播、对象是否串用 |
| 查询结果缓存 | 重复查询、统计结果 | 查询键失效、短TTL、数据版本 | 参数与权限是否进入键 |
| CDN边缘缓存 | 静态资源、允许边缘共享的页面 | 刷新、版本化URL、TTL | 不同节点是否更新、回源比例 |
例如,商品详情页包含商品介绍、登录用户价格和购物车数量。公开介绍可以缓存,但整个页面未必能共享。更合理的方案可能是缓存公共页面骨架,让用户价格和购物车接口单独请求。

判断能否使用共享缓存,关键不是URL是否相同,而是不同用户在同一个缓存键下,是否应当得到相同响应。
香港服务器只是部署位置,并不改变这一原则。面向不同地区访问者时,命中缓存可以减少香港节点到源站的往返及应用处理时间;访问者到香港节点的网络时延仍然存在。
为什么不建议直接缓存整个网站
整站启用缓存容易把登录态、购物车、管理后台和用户专属接口放入共享区域。即使源站通常返回正确的 Cache-Control,也不应该把数据隔离完全寄托在某一个响应头上。
较稳妥的起点是:
- 仅对白名单路径启用缓存。
- 只缓存GET和HEAD请求。
- 带Cookie或Authorization的请求先绕过缓存。
- 不忽略源站的
Set-Cookie、Cache-Control和Vary。 - 登录、支付、管理后台及用户专属数据默认不进入共享缓存。
“所有Cookie都绕过”会降低命中率,却便于建立安全基线。后续如需允许统计类Cookie,可以在明确业务语义后细化规则,而不是直接忽略全部Cookie。
二、工作机制:从一个请求理解配置如何生效
缓存键决定哪些请求能够复用
请求进入Nginx后,缓存模块根据缓存键查找对象。没有可用对象时,请求转发给上游;响应符合缓存条件,才会被写入缓存。

一个简单的公开内容缓存键可以是:
proxy_cache_key "$scheme|$host|$request_uri|public-v1";
这里分别保留协议、主机名、完整请求URI和缓存命名空间版本。$request_uri 包含原始查询字符串,因此:
/public/catalog.json?page=1
/public/catalog.json?page=2
不会混用同一份响应。
代价是参数顺序不同也可能生成不同对象,例如 ?page=1&sort=price 与 ?sort=price&page=1。先保证正确性,再考虑参数规范化。不能为了提高命中率,直接从缓存键删除会影响内容的参数。
示例没有加入请求方法,是因为只允许GET、HEAD参与缓存,并保留Nginx默认的HEAD转GET缓存行为。若修改该行为,或者让其他方法进入缓存,就必须重新审查方法与缓存键的关系。
TTL、淘汰和过期复用分别解决什么问题
这三个概念容易混淆:
- TTL:对象在多长时间内可以被视为新鲜。
- inactive:对象在多长时间未被访问后,可以从缓存中移除。
- 过期复用:对象过期后,在更新或上游故障期间,是否仍允许返回旧内容。
inactive=30m 不意味着内容可以新鲜使用30分钟。它描述的是未访问对象的清理条件,而不是内容更新时间。
同样,允许 stale 后,TTL也不再是“旧内容最多出现多久”的严格承诺。对于库存、权限、价格承诺等要求及时更新的数据,不应套用公开资讯页的过期复用策略。
准备条件与可执行配置
以下示例用于Linux上的Nginx测试环境:上游应用监听 127.0.0.1:9000,公开测试接口为 /public/catalog.json,Nginx测试入口仅监听本机8080端口。该接口应返回稳定的公共内容,不应按Cookie、Authorization或访问者身份生成差异数据。
先核对实际安装和配置布局:
nginx -v
nginx -V 2>&1
sudo nginx -T
ps -eo user,args | grep '[n]ginx: worker'
df -h /var/cache
df -i /var/cache
nginx -T 用于确认配置文件及其包含关系,也可能显示内部地址等信息,不要直接公开完整输出。
缓存目录必须允许worker用户写入。下面仅适用于已确认worker用户和组均为 www-data、且目标目录尚未创建的环境:
getent passwd www-data
getent group www-data
sudo install -d -o www-data -g www-data -m 0750 /var/cache/nginx/a5-public
此操作只针对新建的缓存目录,不应递归修改现有业务目录权限。若worker身份不同,应替换用户和组;若启用了SELinux或AppArmor,还需核对相应访问规则。回退时先停用引用该目录的配置,确认没有使用者,再按变更记录处理目录,不要直接清空正在使用的缓存。
修改前备份配置:
sudo cp -a /etc/nginx "/root/nginx-backup-$(date +%Y%m%d-%H%M%S)"
下面配置必须放在 http 上下文内。例如,只有确认 conf.d/*.conf 在 http 内被包含,才可放入相应文件;不要直接追加到 server 或 location 内。
proxy_cache_path /var/cache/nginx/a5-public
levels=1:2
keys_zone=a5_public:64m
max_size=5g
inactive=30m
use_temp_path=off;
map $request_method $skip_method {
default 1;
GET 0;
HEAD 0;
}
map $http_cookie $skip_cookie {
default 1;
"" 0;
}
map $http_authorization $skip_auth {
default 1;
"" 0;
}
map "$skip_method$skip_cookie$skip_auth" $skip_cache {
default 1;
"000" 0;
}
log_format a5_cache
'$time_iso8601 host=$host method=$request_method uri=$uri '
'status=$status cache=$upstream_cache_status '
'rt=$request_time urt=$upstream_response_time '
'upstream=$upstream_addr us=$upstream_status';
upstream a5_origin {
server 127.0.0.1:9000;
keepalive 32;
}
server {
listen 127.0.0.1:8080 default_server;
server_name _;
return 404;
}
server {
listen 127.0.0.1:8080;
server_name cache-demo.example.com;
access_log /var/log/nginx/a5-cache-access.log a5_cache;
location ^~ /public/ {
proxy_pass http://a5_origin;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 2s;
proxy_read_timeout 10s;
proxy_cache a5_public;
proxy_cache_key "$scheme|$host|$request_uri|public-v1";
proxy_cache_methods GET HEAD;
proxy_cache_valid 200 60s;
proxy_cache_bypass $skip_cache;
proxy_no_cache $skip_cache;
proxy_cache_lock on;
proxy_cache_lock_timeout 5s;
proxy_cache_lock_age 5s;
proxy_cache_background_update on;
proxy_cache_use_stale updating error timeout
http_500 http_502 http_503 http_504;
add_header X-Cache-Status $upstream_cache_status always;
}
location / {
proxy_pass http://a5_origin;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $host;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
这个示例有几个重要约束:
keys_zone=64m 保存缓存索引等元数据,不代表响应体全部存放在64MB内存中;响应内容主要存放在缓存目录,操作系统可能通过页缓存加速读取。max_size=5g 是缓存管理目标,不是写入瞬间不可超过的硬限制,应额外预留磁盘空间。
proxy_cache_bypass 控制是否读取已有缓存,proxy_no_cache 控制是否保存本次上游响应。带登录态请求需要同时考虑这两个方向,避免出现“没有读取缓存,却把个人响应写进去”的情况。
proxy_cache_valid 200 60s 给出200响应的配置级缓存时间,但源站的 X-Accel-Expires、Cache-Control、Expires 等可能影响实际缓存行为。示例没有覆盖这些头,也没有忽略 Set-Cookie 或 Vary,因此一分钟不是所有响应的固定有效期。
缓存锁用于减少同一个冷缓存对象被同时重建。等待超过锁超时、更新请求过慢等情况下,仍可能出现额外回源,不能把它理解为任何情况下都只会有一个上游请求。
完成修改后:
sudo nginx -t
sudo systemctl reload nginx
第二条仅适用于由systemd管理、服务名为 nginx 的环境。语法检查失败时不要reload;需要回退时,恢复本次修改的文件,重新执行 nginx -t,通过后再reload。生产使用时,应将验证过的缓存逻辑迁入实际HTTPS站点,并核对可信主机名及上游Host要求,不要直接开放测试入口。
三、影响因素:命中率之外,还要检查连接和资源
连接数不是并发请求数
高命中率可以显著减少上游连接,但Nginx仍需维持客户端连接。缓存未命中时,一个活动请求通常还会占用上游连接资源。
下面是容量起点,不是所有香港服务器都应照搬的推荐值:
# 主配置顶层
worker_processes auto;
# 放入现有 events 块,不要重复创建 events
events {
worker_connections 4096;
}
worker_connections 是每个worker的连接额度,不只计算客户端连接,还包括上游等连接。HTTP/2又允许同一个连接承载多个请求,所以不能直接把“worker数量×4096”当成业务并发能力。
连接额度还受进程文件描述符限制影响:
systemctl show nginx -p LimitNOFILE
该命令仅适用于对应systemd服务。还应检查实际worker进程:
sudo cat /proc//limits
将 替换为真实PID。文件描述符还用于日志、缓存文件等,不能全部留给网络连接。若日志出现 worker_connections are not enough 或 too many open files,才有针对性地检查连接额度、运行中进程限制及流量来源。
上游keepalive不是连接总上限
示例中的 keepalive 32 表示每个worker为该上游组保留的空闲连接数量,并不限制活动连接总数。它减少频繁建连的成本,但不能替代应用端的连接池、数据库容量和并发限制。
缓存命中时,上游连接需求下降;冷缓存和大规模失效时,需求可能突然恢复。因此,压测只覆盖热缓存,容易高估系统的安全容量。
命中缓存仍然消耗带宽与磁盘资源
以一个估算场景说明:每个响应体为20KB,按十进制计算,1KB为1000字节;每秒返回2000次,则响应体流量为:
20,000字节 × 2,000次/秒 = 40,000,000字节/秒
40,000,000 × 8 ÷ 1,000,000 = 320Mbps
这是未计协议开销的响应体流量,也未考虑压缩和客户端中断。即使全部命中缓存,香港服务器对外发送这些内容仍然需要相应带宽。
大量小对象更容易受到缓存元数据、文件数量和随机I/O影响;大对象则更容易受到磁盘吞吐与出口带宽影响。CPU、带宽和磁盘中任何一项饱和,命中率都可能很好,但响应时间仍然上升。
四、验证方法:证明可缓存、正确命中并能按预期更新
先验证MISS到HIT,而不是直接压测
测试接口应存在并返回200,源站不应设置禁止缓存的头,也不应返回 Set-Cookie。以下命令发起GET请求,只丢弃响应体,不使用 curl -I,避免把HEAD行为与GET验证混在一起:
HOST=cache-demo.example.com
URL=http://127.0.0.1:8080/public/catalog.json
curl -sS -H "Host: $HOST" -D - -o /dev/null "$URL"
curl -sS -H "Host: $HOST" -D - -o /dev/null "$URL"
对于此前不存在的新鲜测试对象,预期先出现:
X-Cache-Status: MISS
随后出现:
X-Cache-Status: HIT
如果一直是MISS,应先查看状态码及源站响应头,再检查目录权限和错误日志。常见原因包括 Cache-Control: private/no-store/no-cache、Set-Cookie、Vary: *、源站自定义缓存控制,以及每次请求都改变了查询参数。

若显示BYPASS,则优先核对方法、Cookie和Authorization;若没有缓存状态,则检查请求是否进入了启用缓存的location。浏览器刷新、CDN和本地浏览器缓存可能干扰观察,本机直连测试有助于隔离这些因素。
必须验证隔离,不能只看到HIT就算成功
curl -sS -H "Host: $HOST" \
-H "Cookie: session=test-only" \
-D - -o /dev/null "$URL"
curl -sS -H "Host: $HOST" \
-H "Authorization: Bearer test-only" \
-D - -o /dev/null "$URL"
这两次请求应绕过缓存;若上游接受请求,通常可以看到BYPASS。不要在命令、截图或日志中使用真实令牌。
还应比较不同查询参数的响应体,确认它们没有串用缓存。对于多语言、地区定价或自定义请求头影响内容的接口,需要另外检查 Vary 或显式缓存键设计。并非所有业务差异都能由Nginx自动识别。
结合日志解释命中和回源
查看测试日志:
sudo tail -n 50 /var/log/nginx/a5-cache-access.log
以下是说明逻辑的示例结果,不代表本站实测:
status=200 cache=MISS rt=0.083 urt=0.081 upstream=127.0.0.1:9000 us=200
status=200 cache=HIT rt=0.002 urt=- upstream=- us=-
第一行说明该请求访问了上游,第二行说明响应来自缓存。request_time 包含Nginx处理及向客户端发送等时间,不等于纯应用计算耗时;客户端接收较慢时,HIT请求也可能持续较久。
命中率的分母应先说明范围:统计“允许缓存的公共GET请求”,与统计“整个网站请求”,会得到不同结果。应将HIT、MISS、BYPASS、EXPIRED、STALE、UPDATING分别观察,避免把业务绕过或故障复用误当成普通命中。
验证TTL、后台更新与失效
若源站没有覆盖配置级TTL,可以在缓存建立后等待超过60秒,再请求同一URL。由于示例开启后台更新和过期复用,首次请求可能返回STALE,更新期间的其他请求可能返回UPDATING,更新完成后再出现HIT。
状态受到请求时序影响,不必强求每次都出现同一序列。真正需要核对的是:
- 源站内容更新后,新内容最终能否进入缓存。
- 更新期间允许返回旧内容多久。
- 上游失败时,返回旧内容是否符合业务规则。
需要紧急失效时,可将键中的 public-v1 改为 public-v2,通过语法检查后reload。新请求会进入新的缓存命名空间,旧文件等待缓存管理器清理,因此会产生冷缓存流量和额外磁盘占用。
这不是定向刷新接口,也不应为刷新一个页面就频繁修改全站版本。不要默认普通安装已具备可调用的清理接口,应先核对版本、模块和访问控制。
最后进行受控并发验证
在获授权的测试环境中,确认已安装 wrk,可先预热同一个URL,再进行短时测试:
wrk -t4 -c100 -d30s \
-H "Host: cache-demo.example.com" \
http://127.0.0.1:8080/public/catalog.json
从较低并发逐步增加,同时观察错误率、响应延迟、Nginx日志、应用请求量、CPU、磁盘和网络。分别测试热缓存、冷缓存及接近过期时的行为,不要只测持续HIT。

本机压测适合隔离服务端处理能力,但压测工具也消耗同一台机器的资源,且不代表真实访问者到香港的链路表现。外部验收应从目标访问地区测试,并区分DNS、连接、TLS、首字节和完整下载时间。
五、适用限制:把性能收益与数据新鲜度一起验收
对公开文章、公共商品介绍、更新频率不高的JSON响应,短TTL加缓存锁通常是容易验证的起点;允许短时旧内容的业务,可以进一步评估后台更新和故障复用。
对账户余额、订单状态、权限判断、实时库存和用户专属价格,更合适的起点是“不使用共享HTTP缓存”,或者先在应用层设计严格的身份隔离和失效机制。Nginx不知道某次数据库更新应让哪些页面同时失效,单靠TTL无法保证跨页面一致性。
如果香港服务器前面还有CDN,必须分别验证两层。Nginx的 X-Cache-Status 响应头也可能被CDN缓存,边缘返回的HIT字样不一定代表本次请求真的到达Nginx。应结合CDN自身状态、Nginx访问日志和源站日志确认路径。
上线验收可以围绕四个问题完成:公共请求是否稳定命中,身份请求是否正确绕过,更新是否在业务允许的时间内生效,冷缓存和故障期间上游是否仍能承受回源。四项都通过后,再提高缓存覆盖范围和并发额度;若只是HIT变多,却出现错误内容、长时间旧数据或带宽饱和,就不应继续以命中率作为优化目标。



