Nginx 缓存配置完全指南(实战总结)
Nginx 视频缓存配置完全指南
整理于 2026-07-01,基于个人运维平台视频服务的缓存配置实战排查全过程。
一、架构总览:一条请求的完整路径
你打开浏览器 → 请求到 Nginx → Nginx 问 Flask → Flask 渲染页面返回
三层缓存体系
① 浏览器缓存(Cache-Control)
↓ 没命中?
② Nginx proxy_cache(X-Cache: HIT/MISS) ← 本文重点
↓ 没命中?
③ Flask 渲染页面(最慢)
| 资源类型 | 走哪条路 | 缓存谁控制 | 怎么看 |
|---|---|---|---|
| HTML 页面(/videos、/note/1) | → Nginx → Flask | Nginx 的 proxy_cache_valid | X-Cache: HIT |
| 静态文件(.css、.js、图片) | → Nginx 直接读磁盘 | Nginx 的 expires + Cache-Control | Cache-Control: max-age=... |
| B站播放器(iframe 嵌入) | → 直接请求 B站服务器 | B站的 CDN(你管不了) | F12 → Network |
二、核心概念
2.1 什么是 Nginx 缓存(proxy_cache)
Nginx 把后端(Flask)返回的页面存一份在磁盘上。下次同样的请求进来,Nginx 直接返回缓存,不再问 Flask。
1. proxy_pass 负责转给谁处理,没有它,Nginx 就自己读磁盘/返回静态文件(如 /static/)。有了它,才是"反向代理"。
2. proxy_cache 是 Nginx 做反向代理时,在代理层(Nginx 和后端应用之间)加的一层缓存。负责要不要缓存后端的结果。
proxy_cache micro_cache; #缓存到磁盘
proxy_cache_valid 200 60s; #缓存策略
命中 → 响应头出现 x-cache: HIT;未命中 → x-cache: MISS
3. proxy_ignore_headers — 后端响应头,但Nginx不听
4. add_header — 返回客户端前,额外加个头,只影响返回给客户端的响应,不影响 Nginx 内部的缓存逻辑。
动态资源(/videos/ /note/)流程:
1. 请求到达 Nginx
2. Nginx 通过proxy_pass反向代理指向proxy_cache代理缓存,根据 proxy_cache_key 生成缓存 key(key每次都不一样),先去磁盘缓存里查有没有
3. 有且未过期(x_cache:HIT) → 直接返回缓存,不再往后端转发
4. 没有或已过期(x_cache:MISS) → 转发给 Flask 后端,拿到响应后写入缓存,再返回给客户端
静态资源(static/css/js/图片)流程:
1.请求到达Nginx
2.Nginx直接读磁盘,缓存策略信息Cache-Control、Expires
3.返回给客户端,没有x_cache
2.2 缓存命中的三种状态
| 状态 | 翻译 | 意味着什么 | 你该怎么做 |
|---|---|---|---|
| HIT | 命中了 | Nginx 直接返回缓存内容,没找 Flask。响应快,服务器省力 | 正常状态。如果用户说看不到新内容,调短 TTL |
| MISS | 没命中 | Nginx 缓存里没有,去问了 Flask。第一次访问或缓存已过期 | 偶尔 MISS 正常;总是 MISS 说明配置可能有问题 |
| BYPASS | 跳过了 | 这个请求 Nginx 故意不走缓存(如 POST、HEAD) | 确认请求方式,非 GET 不用管 |
2.3 浏览器缓存 vs Nginx 缓存(疑惑点对比)
| 对比项 | 浏览器缓存 | Nginx 缓存(proxy_cache) |
|---|---|---|
| 存在哪里 | 用户本地浏览器 | 服务器磁盘(/var/cache/nginx/) |
| 命中了会怎样 | 不发请求到服务器 | 发请求到 Nginx,但 Nginx 不找 Flask |
| 怎么看 | Cache-Control 响应头 | X-Cache-Status 响应头 |
| 谁控制 | expires 指令 | proxy_cache_valid 指令 |
2.4 proxy_ignore_headers 与 add_header 的区别(疑惑点对比)
这两行配置容易混淆:
proxy_ignore_headers Cache-Control Expires; ← ① Nginx 自己内部用 # 忽略后端返回的缓存头
add_header X-Cache-Status $upstream_cache_status; ← ② 返回给客户端的
| 配置 | 作用对象 | 给谁看的 |
|---|---|---|
| proxy_ignore_headers | Nginx 自己做缓存决策时用 | 内部用的,与外面无关 |
| add_header X-Cache-Status | 返回给客户端的响应 | 给你(curl/浏览器)看的 |
proxy_ignore_headers 的意思是: Flask 返回的 Cache-Control 和 Expires,Nginx 做缓存决策时当没听见。Nginx 只认自己的 proxy_cache_valid。
X-Cache-Status 不是 Flask 给的,是 Nginx 自己加的变量。
2.5 Range 请求缓存
Range 请求是视频拖进度条时,播放器只请求某一小段数据(如 "bytes=0-1024")。
| 场景 | 结果 |
|---|---|
| 开启 Range 缓存 | 用户拖进度条 → Nginx 缓存里有 → 直接返回,秒拖 |
| 关闭 Range 缓存 | 用户拖进度条 → 每次都回源 → 卡顿、源站压力大 |
服务器返回 HTTP 206 和 Content-Range 头表示这是一个范围响应。
三、实战命令大全
3.1 查看缓存状态(核心命令)
# 一条命令搞定:看响应头中的缓存状态
curl -sk https://zhoujiayi.xyz/videos -D - -o /dev/null 2>&1 | grep x-cache
-s:不显示进度条和错误信息;-k:跳过 SSL 证书验证
-D:表示只输出响应头(response headers);- 表示输出到屏幕
-o /dev/null:正文扔垃圾桶
2>&1:把错误信息塞进正确信息里
| grep x-cache:筛选出包含 x-cache 的行
# 输出示例
# x-cache-status: HIT
# x-cache-status: MISS
# x-cache-status: BYPASS
3.2 curl 参数详解
| 参数 | 作用 |
|---|---|
| -s | 安静模式,不显示进度条 |
| -k | 跳过 HTTPS 证书检查(自己的服务器不用纠结) |
| -I | HEAD 请求,只要响应头不要正文 |
| -D - | 把响应头打印到屏幕上 |
| -o /dev/null | 正文扔垃圾桶,只看头 |
| -o /dev/null -w "%{http_code}" | 只看状态码 |
| 2>&1 | 错误信息也一起显示 |
| grep "关键词" |
从结果中筛选关键词 |
3.3 参数组合对比(疑惑点对比)
| 命令 | 正文去哪了 | 头去哪了 | 适用场景 |
|---|---|---|---|
| curl -sk https://... | 打印到屏幕 | 也打印到屏幕(混在一起) | 想看完整内容 |
| curl -sk https://... -I | 不要正文 | 打印到屏幕 | 只看头信息(但发的是 HEAD 请求,可能 BYPASS 缓存) |
| curl -sk https://... -o /dev/null | 扔掉 | 也扔了 | 只看返回状态码(加 -w) |
| curl -sk https://... -D - -o /dev/null | 扔掉 | 打印到屏幕 | 最常用的排查命令 |
注意: -I 发的是 HEAD 请求,如果 Nginx 配置了 if ($request_method != GET) 就会返回 BYPASS。所以排查缓存建议用 -D - 而不是 -I。
3.4 Nginx 配置相关命令
# 查看当前视频缓存的 TTL
grep "proxy_cache_valid" /etc/nginx/conf.d/ops-platform.conf
# 修改 TTL(例如 300s → 600s)
sed -i 's/proxy_cache_valid 200 300s;/proxy_cache_valid 200 600s;/' /etc/nginx/conf.d/ops-platform.conf
# 检查 Nginx 配置语法
nginx -t
# 热重载 Nginx(不中断现有连接)
nginx -s reload
3.5 日志分析命令
# 查看最近几条请求日志
tail -5 /var/log/nginx/access.log
# 统计缓存命中次数
grep -c " HIT" /var/log/nginx/access.log
grep -c " MISS" /var/log/nginx/access.log
grep -c " BYPASS" /var/log/nginx/access.log
# 看实时请求(持续跟踪)
tail -f /var/log/nginx/access.log
# 查看 Nginx 错误日志(排查 403/500 等问题)
tail -20 /var/log/nginx/error.log
3.6 日志格式解读
一条典型的访问日志:
43.134.84.11 - - [01/Jul/2026:18:48:39 +0800] "GET /videos HTTP/2.0" 200 13787 HIT upstream=- total=0.000
| 字段 | 值 | 意思 |
|---|---|---|
| 43.134.84.11 | 访问者 IP | 谁访问的 |
| "GET /videos" | 请求方法 + 路径 | 访问了哪个页面 |
| 200 | 状态码 | 正常返回 |
| 13787 | 字节数 | 页面大小 |
| HIT | 缓存状态 | 命中了缓存,没找 Flask |
| upstream=- | Flask 耗时 | - 表示根本没找 Flask,0ms |
| total=0.000 | 总耗时 | 0 秒,秒回 |
MISS 的日志对比:
... "GET /videos/16" 200 16315 MISS upstream=0.010 total=0.010
- upstream=0.010 → Flask 花了 10ms 渲染
- total=0.010 → 总共 10ms
3.7 静态文件排查
# 查看静态文件是否能正常加载
curl -skI https://zhoujiayi.xyz/static/css/style.css
# 检查文件路径权限链
namei -l /root/note-center/static/css/style.css
# 修复权限(Nginx 以 nginx 用户运行,需要能进入路径)
chmod o+rx /root
# 模拟 Range 请求(视频拖进度条)
curl -sk https://zhoujiayi.xyz/static/css/style.css -H "Range: bytes=0-1024" -D - -o /dev/null 2>&1 | grep -i "http/\|content-range"
四、Nginx 缓存配置结构
4.1 视频服务专属缓存规则
# 视频列表页:5 分钟缓存
location /videos {
proxy_cache micro_cache; # 使用上面定义的缓存区
proxy_cache_key "$scheme$host$request_uri";
proxy_cache_valid 200 300s; # 200 状态的响应缓存 300 秒
proxy_cache_valid 404 5s;
proxy_cache_use_stale error timeout updating;
proxy_cache_lock on;
proxy_ignore_headers Cache-Control Expires;
proxy_pass http://flask_backend;
add_header X-Cache-Status $upstream_cache_status;
}
# 缓存配置与排错指南(内容稳定,10 分钟缓存)
location ~ ^/(video-cache|video-troubleshooting) {
proxy_cache micro_cache;
proxy_cache_valid 200 600s;
...
}
4.2 各页面缓存策略对比
| 页面 | 缓存时长 | 理由 |
|---|---|---|
| 视频列表 /videos | 5 分钟 | 不会频繁增删,5 分钟足够 |
| 播放页 /videos/16 | 5 分钟 | 播放次数 5 分钟后自动更新 |
| 缓存配置 /video-cache | 10 分钟 | 几乎不动的内容 |
| 排错指南 /video-troubleshooting | 10 分钟 | 纯文档,几乎不改 |
| 黄金价格 /gold | 1 小时 | 每天只更新一次 |
| 笔记查看 /note/\d+ | 5 分钟 | 笔记内容相对稳定 |
| 监控面板 /monitor | 不缓存 | 需要保持实时 |
| 静态文件 /static/ | 30 天(浏览器缓存) | 文件不变,长久缓存 |
4.3 生产环境最佳实践
proxy_cache_path /var/cache/nginx/micro levels=1:2 keys_zone=micro_cache:5m max_size=256m inactive=5m;
参数含义:
- keys_zone=micro_cache:5m:缓存索引在内存中占 5MB
- max_size=256m:缓存文件最大占 256MB 磁盘
- inactive=5m:5 分钟没被访问的缓存自动清除
五、完整排查流程
5.1 用户说"慢"
1. curl -sk https://域名/页面 -D - -o /dev/null 2>&1 | grep x-cache
→ HIT:慢的不是 Nginx,可能是 B站播放器加载慢(外部原因)
→ MISS:缓存没命中,去查 Flask 了
2. 如果一直 MISS,检查 TTL:
grep "proxy_cache_valid" /etc/nginx/conf.d/ops-platform.conf
3. 如果 TTL 太短,调整:
sed -i 's/旧值/新值/' 配置文件名
nginx -t && nginx -s reload
5.2 用户说"看不到新内容"
1. 先 curl 查缓存状态
→ HIT:说明 Nginx 返回了旧缓存 → TTL 太长 → 调短
2. 或者手动预热/刷新缓存
curl -sk https://域名/页面 -o /dev/null # 预热
# CDN 厂商控制台执行 URL 刷新(如果有 CDN)
5.3 页面样式丢了
1. 检查静态文件能否加载
curl -skI https://域名/static/css/style.css
2. 如果返回 403,看 Nginx 错误日志
tail -20 /var/log/nginx/error.log
→ Permission denied → 检查文件路径权限
3. 修复权限
namei -l /root/note-center/static/css/style.css # 看哪一级目录权限不足
chmod o+rx /目录名 # 给 nginx 用户加进入权限
六、核心原则
6.1 关于缓存命中率
- HIT 是常态,MISS 是过程,连续 MISS 才是问题
- 缓存命中率 = HIT 次数 ÷(HIT + MISS)次数 × 100%
- 命中率 > 70% 为良好
情况一:MISS 是你想解决的"问题"
比如首页 / 配了 60s 缓存,但每次请求都是 MISS,说明缓存根本没用上。排查方向: - 先看是什么导致 key 不同
tail -100 /var/log/nginx/access.log | grep MISS
如果 URL 参数每次都不一样 → 是请求方的问题,不是服务器的问题(你之前遇到的情况) - 检查是不是后端返回了干扰头
curl -skI https://zhoujiayi.xyz/ | grep -iE 'cache-control|expires|set-cookie|no-cache'
如果 Flask 返回了 Cache-Control: no-cache 或 Set-Cookie,Nginx 默认不会缓存。
✅ 解决:你已经配了 proxy_ignore_headers Cache-Control Expires Set-Cookie;,这个没问题了。 - 检查缓存空间是不是满了
df -h /var/cache/nginx/micro # 看分区有没有满
du -sh /var/cache/nginx/micro # 当前缓存占了多少
你的配置 max_size=256m,如果满了会淘汰旧条目,但不会导致全 MISS。 - 缓存文件权限
namei /var/cache/nginx/micro
确保 Nginx worker 进程有读写权限。
情况二:MISS 本身不是问题,看命中率。但只能看总数
grep -c " HIT" /var/log/nginx/access.log # → 3
grep -c " MISS" /var/log/nginx/access.log # → 24
6.2 关于 TTL 设置
- 内容越稳定,TTL 越长
- 热门视频可短些,冷门可长些
- 浏览器缓存 < CDN 节点缓存 < 源站缓存
- 视频服务建议:浏览器 1h,CDN 1d,源站 7d
6.3 关于生产环境
- 开发环境(Flask 自带服务器):无缓存,适合调试
- 生产环境(Nginx + Flask):加缓存,加日志,加监控
- 你的架构中 CDN 边缘节点 = Nginx 服务器本身(只有 1 台,不是真正的多节点 CDN)
七、日常自查清单
# ① 看缓存是否命中
curl -sk https://zhoujiayi.xyz/videos -D - -o /dev/null 2>&1 | grep x-cache
# ② 看当前 TTL 配置
grep "proxy_cache_valid" /etc/nginx/conf.d/ops-platform.conf
# ③ 看日志中的 HIT/MISS 统计
grep -c " HIT" /var/log/nginx/access.log
grep -c " MISS" /var/log/nginx/access.log
# ④ 看最近请求
tail -5 /var/log/nginx/access.log
八、实例演练:保存笔记后仍显示旧内容
### 1、问题现象
在 Note Center 编辑笔记 → 点保存 → 页面重定向回笔记页 → 显示的还是旧内容。
`Ctrl+F5` 无效,curl 查却能拿到新内容。
###2、排查步骤
第一步:确认是不是你自己电脑的问题
按 Ctrl + F5(或 Cmd + Shift + R)硬刷新一次。
- 如果变新内容了 → 就是浏览器缓存了旧页面
- 如果还是旧的 → 往下走
第二步:确认 Nginx 是否缓存了旧页面
在命令行跑一次,不走浏览器:
curl -sk https://zhoujiayi.xyz -D - -o /dev/null 2>&1 | grep -i x-cache
看输出是 HIT 还是 MISS 还是没内容。
- 没有 x-cache(或 -) → 说明没走缓存层,往下走
- HIT → 说明 Nginx 返回了它缓存里的旧页面
- MISS / EXPIRED → 说明 Nginx 没有缓存或已过期,往下走
第三步:确认页面内容到底更新了没有
curl -sk https://zhoujiayi.xyz | grep '你改的文字(取一小段)'
- 如果搜到新文字 → 后端已经更新了,问题在前端(浏览器缓存或 CDN)
- 如果还是旧文字 → 后端确实没更新,问题出在保存环节
第四步:找到问题所在
curl 搜到新文字,浏览器是旧的——浏览器缓存——Ctrl+F5 就行
curl 搜到新文字,但 x-cache 显示 HIT——Nginx 缓存没过期——清 Nginx 缓存
curl 也是旧文字——根本没保存成功——检查保存逻辑或数据库
### 3、请求链路还原
grep '/note/38' /var/log/nginx/access.log | tail -10
```
01:19:55 ← 你看笔记 → /note/38 → HIT(Nginx 缓存了旧版本,有效期 10 分钟)
01:20:04 ← 进编辑页 → /note/38/edit
01:22:19 ← 点保存 → 302 跳到 /note/38?saved=1 → MISS(拿到了新内容)
但此刻 /note/38(不带 ?saved=1)的旧缓存还在!
01:21:39 ← 刷新 /note/38(没参数)→ 全 HIT → 旧内容
01:25:24 ← 10 分钟后缓存过期了 → EXPIRED → 拿到新内容
```
### 4、根因分析
浏览器发请求到 Nginx → Nginx 看自己的缓存还有效(HIT)→ 直接返回旧的 → 浏览器收到旧内容。Ctrl+F5 只清你浏览器的缓存,Nginx 服务器上的 proxy_cache 它管不着。
第二次保存的时间点,10 分钟缓存已经过期了(01:25:24 已经开始 EXPIRED 了),所以浏览器拿到的是新数据。
1. 第一次保存,Flask 写数据库,返回 302 重定向到 `/note/38?saved=1` (?saved=1改变页面内容/行为)
2. /note/38 之前已经缓存了 10 分钟
3. `?saved=1` 改变了缓存 key,所以 `/note/38?saved=1` 能拿到新内容
4. 但 `/note/38`(不带参数)的旧缓存条目还活着(TTL 10 分钟)
5. 任何对 `/note/38` 的请求依然命中旧缓存
6. `Ctrl+F5` 只清浏览器缓存,管不到 Nginx 的服务端缓存
### 解法
● 方案一(推荐)**:在笔记 location 也加上 saved=1 的 bypass:
```nginx
location ~ ^/note/\d+$ {
proxy_cache micro_cache;
proxy_cache_valid 200 600s;
proxy_cache_valid 404 5s;
proxy_cache_use_stale error timeout updating;
proxy_cache_lock on;
proxy_ignore_headers Cache-Control Expires;
# 保存/删除/恢复后跳过并清缓存
set $cache_bypass "";
nginx: configuration file /etc/nginx/nginx.conf test is successful
Thought for 1s (ctrl+o to expand)
● 方案二:
该proxy_cache_valid
- /note/ 缓存时间:~~600s~~ → 200s
- 配置检查通过,已重新加载
保存笔记后最多等 200 秒(3 分 20 秒),缓存就会过期,刷新就能看到新内容了。
写在最后
Nginx 缓存排查的核心就是三句话:
curl -D - -o /dev/null | grep x-cache— 实时看缓存状态grep proxy_cache_valid— 看当前 TTL 配了多少nginx -t && nginx -s reload— 改完配置后加载生效