☰
👁️ 预览
取消
💾 保存
C
Claude
▾
C
Claude
claude@note.center
🔔
消息通知
🔄
账号切换
⚙️
设置
🚪
退出登录
Markdown
富文本
# 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 的区别(疑惑点对比) 这两行配置容易混淆: ```nginx 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 查看缓存状态(核心命令) ```bash # 一条命令搞定:看响应头中的缓存状态 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 配置相关命令 ```bash # 查看当前视频缓存的 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 日志分析命令 ```bash # 查看最近几条请求日志 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 静态文件排查 ```bash # 查看静态文件是否能正常加载 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 视频服务专属缓存规则 ```nginx # 视频列表页: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 生产环境最佳实践 ```nginx 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,说明缓存根本没用上。排查方向: 1. 先看是什么导致 key 不同 tail -100 /var/log/nginx/access.log | grep MISS 如果 URL 参数每次都不一样 → 是请求方的问题,不是服务器的问题(你之前遇到的情况) 2. 检查是不是后端返回了干扰头 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;,这个没问题了。 3. 检查缓存空间是不是满了 df -h /var/cache/nginx/micro # 看分区有没有满 du -sh /var/cache/nginx/micro # 当前缓存占了多少 你的配置 max_size=256m,如果满了会淘汰旧条目,但不会导致全 MISS。 4. 缓存文件权限 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) --- ## 七、日常自查清单 ```bash # ① 看缓存是否命中 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 ``` --- ## 八、实例演练:保存笔记后仍显示旧内容 ```bash ### 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 缓存排查的核心就是三句话: 1. **`curl -D - -o /dev/null | grep x-cache`** — 实时看缓存状态 2. **`grep proxy_cache_valid`** — 看当前 TTL 配了多少 3. **`nginx -t && nginx -s reload`** — 改完配置后加载生效
预览
编辑内容后实时预览...