美国服务器部署视频站时,页面缓存与视频对象缓存应如何分层设置失效规则?
美国服务器部署视频站时,页面缓存与视频对象缓存不应使用同一套失效规则。更稳妥的分层方式是:匿名页面采用短时间缓存,并在内容发布、登录状态或个性化条件变化时主动失效;视频切片、封面和可下载文件按照“内容是否会被原路径替换”决定缓存时长,使用版本化路径的对象可以长期缓存,直播播放列表则必须保持较短的失效周期。
如果站点前面还有边缘缓存,建议形成“浏览器—边缘缓存—美国服务器本地缓存—应用或对象存储”的层级;如果没有边缘缓存,则由美国服务器上的 Nginx 反向代理承担中间缓存。无论采用哪种结构,页面的新鲜度和视频对象的新鲜度都要分别计算,不能因为视频文件较大,就把 HTML 页面也缓存数小时,更不能因为页面需要及时更新,就让每个视频切片都频繁回源。

先确定每一类内容应该缓存多久
缓存时间不是越长越好,关键在于原 URL 对应的内容是否会被覆盖,以及内容变化后能否通过更新 URL 或主动清理快速生效。
| 内容类型 | 典型内容 | 推荐缓存层级 | 参考失效规则 | 主要失效方式 |
|---|---|---|---|---|
| 公共页面 | 首页、分类页、公开视频详情页 | 浏览器、边缘、美国服务器本地缓存 | 浏览器 5~30 秒,边缘 30~120 秒 | 发布后清理页面 URL,或等待短 TTL |
| 个性化页面 | 登录后的订阅页、播放记录、账户页 | 通常不做共享缓存 | private 或 no-store | 不进入共享缓存 |
| 直播播放列表 | .m3u8、.mpd | 边缘和源站短缓存 | 通常 1~5 秒 | 短 TTL、重新验证 |
| 直播切片 | .ts、.m4s | 边缘、美国服务器对象缓存 | 约 30~120 秒,或为切片时长的 2~5 倍 | 不重复使用旧切片 URL |
| 点播播放列表 | 点播 .m3u8、.mpd | 可缓存 | 未版本化时 30 秒~10 分钟;版本化后可更长 | 清理播放列表或更新版本路径 |
| 版本化点播对象 | 视频切片、MP4、封面、字幕 | 各层均可长期缓存 | 1 天~1 年,常见为 30 天以上 | 发布新版本 URL,不覆盖旧对象 |
| 带鉴权参数的对象 | 带签名或权限校验的媒体 URL | 默认谨慎缓存 | 未确认共享安全前不做共享缓存 | 由鉴权层和业务逻辑控制 |
这里的时间是部署初期的参考值,不是固定标准。真正的判断依据是:页面允许最多陈旧多久,直播播放列表多久必须出现新片段,以及旧视频 URL 是否会被原地覆盖。
例如,一个 6 秒一片的直播流,如果播放列表缓存 60 秒,播放器可能连续多次拿到旧列表,表现为延迟升高或无法及时追上直播。相反,直播切片一旦使用递增序号且不重复覆盖,切片本身不需要像播放列表一样频繁失效。
为什么页面和视频对象不能共用一个 TTL
页面和视频对象的变化方式完全不同。
公共 HTML 页面通常包含标题、播放量、推荐内容、审核状态或广告位等信息。页面本身体积不大,但内容更新频率可能较高,且有些请求带有 Cookie、登录状态或地区化展示逻辑。如果把页面缓存时间设置为 24 小时,用户看到旧标题或旧审核状态只是体验问题;如果把包含用户信息的页面放入共享缓存,则可能产生内容串用户的风险。
视频对象则相反。点播视频通常被切分成许多独立文件,例如:
/hls/vod/movie-202610/v7/seg-00001.m4s
/hls/vod/movie-202610/v7/seg-00002.m4s
/hls/vod/movie-202610/v7/index.m3u8
其中 v7 表示内容版本。只要新编码版本使用 v8,旧切片就不会被新内容覆盖,缓存节点即使保留旧对象,也不会影响新播放。此时最有效的失效方法不是逐个删除旧切片,而是让新的播放列表引用新版本路径。
因此,页面更适合使用“短 TTL + 主动清理或重新验证”,版本化视频对象更适合使用“长 TTL + 永不原地覆盖”。真正需要短 TTL 的视频内容,主要是直播播放列表和会被原路径更新的播放列表文件。
在开始配置前,先确认四个条件
1. 是否区分直播与点播
至少要把以下 URL 分类分开:
/hls/live/ 直播播放列表和直播切片
/hls/vod/ 点播播放列表和点播切片
/video/ 视频详情页面
/api/ 播放状态、评论、收藏等接口
如果直播和点播共用同一套路径,建议先增加路径前缀或通过文件命名规则区分,否则后续很难为播放列表、切片和页面设置不同规则。
2. 点播对象是否版本化
优先使用版本号、发布日期、内容哈希或编码批次标识,例如:
/video/123/v3/seg-001.m4s
/video/123/v4/seg-001.m4s
不要上传新文件后继续覆盖:
/video/123/seg-001.m4s
原地覆盖会让不同缓存层保留不同版本。即使源站已经更新,浏览器、边缘缓存或美国服务器本地缓存仍可能继续返回旧内容。

3. 页面是否包含用户状态
如果页面响应会根据 Cookie、Authorization 请求头、登录状态、订阅权限或用户 ID 变化,就不应直接作为公共页面缓存。可以采用以下策略:
- 公共页面只输出所有访客都相同的内容,允许共享缓存;
- 个性化部分通过前端请求单独获取;
- 账户、播放历史、订阅状态等响应使用
private或no-store; - 如果必须缓存不同版本,确保缓存键包含实际影响内容的维度。
4. 查询参数是否影响内容
缓存键通常会包含完整查询字符串,因此下面两个地址会被视为不同对象:
/video/123/seg-001.m4s?token=a
/video/123/seg-001.m4s?token=b
如果查询参数只是统计参数,例如 utm_source,它会降低缓存命中率;如果查询参数承担签名、权限或清晰度控制,则不能随意删除。尤其不要为了提高命中率,直接把所有媒体请求的缓存键改成 $uri,否则可能把一个带权限的响应错误地复用给其他请求。
如何设置页面、播放列表和视频对象
页面采用短缓存,并避开登录请求
下面是一组适用于 Ubuntu 或 Debian、Nginx 作为反向代理的参考配置。示例中的 127.0.0.1:8080 代表应用服务地址,实际部署时应替换为真实的应用端口。session_id 也要替换为站点实际使用的会话 Cookie 名称。
proxy_cache_path 和 map 放在 Nginx 主配置的 http {} 内,server 放在对应站点配置中。
http {
proxy_cache_path /var/cache/nginx/pages
levels=1:2
keys_zone=page_cache:20m
max_size=2g
inactive=10m
use_temp_path=off;
proxy_cache_path /var/cache/nginx/manifests
levels=1:2
keys_zone=manifest_cache:10m
max_size=1g
inactive=5m
use_temp_path=off;
proxy_cache_path /var/cache/nginx/media
levels=1:2
keys_zone=media_cache:50m
max_size=200g
inactive=7d
use_temp_path=off;
map $request_method $skip_by_method {
default 1;
GET 0;
HEAD 0;
}
map $http_authorization $skip_by_auth {
default 1;
"" 0;
}
map $cookie_session_id $skip_by_session {
default 1;
"" 0;
}
map "$skip_by_method$skip_by_auth$skip_by_session" $skip_page_cache {
default 1;
"000" 0;
}
upstream video_origin {
server 127.0.0.1:8080;
}
server {
listen 80;
server_name video.example.test;
location / {
proxy_pass http://video_origin;
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_cache page_cache;
proxy_cache_methods GET HEAD;
proxy_cache_key "$scheme|$host|$request_uri";
proxy_cache_bypass $skip_page_cache;
proxy_no_cache $skip_page_cache $upstream_http_set_cookie;
proxy_cache_lock on;
proxy_cache_revalidate on;
proxy_cache_use_stale error timeout updating
http_500 http_502 http_503 http_504;
proxy_cache_valid 200 30s;
proxy_cache_valid 301 302 10m;
add_header X-Page-Cache $upstream_cache_status always;
}
}
}
这段配置只对没有登录状态、没有 Authorization 请求头的 GET 或 HEAD 请求启用页面缓存。proxy_no_cache $upstream_http_set_cookie 用于避免应用设置会话 Cookie 后仍被写入共享缓存。
如果站点还使用了 user_id、login_token、member 等 Cookie,只检查 session_id 就不够,需要把这些 Cookie 一并纳入绕过条件。最安全的原则是:只要响应内容可能因请求者不同而变化,就不要让它进入公共页面缓存。
直播播放列表设置为秒级缓存
直播播放列表必须快速更新。可以单独配置 .m3u8 或 .mpd:
server {
listen 80;
server_name video.example.test;
location ~* ^/hls/live/.*\.(m3u8|mpd)$ {
proxy_pass http://video_origin;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache manifest_cache;
proxy_cache_methods GET HEAD;
proxy_cache_key "$scheme|$host|$request_uri";
proxy_cache_valid 200 3s;
proxy_cache_lock on;
proxy_no_cache $http_authorization $upstream_http_set_cookie;
proxy_hide_header Cache-Control;
add_header Cache-Control "public, max-age=0, s-maxage=3, must-revalidate" always;
add_header X-Object-Cache $upstream_cache_status always;
}
}
这里的 3s 只是直播初始值。如果切片时长为 2 秒,可以从 2~5 秒开始观察;如果切片时长为 10 秒,则 3 秒缓存未必有必要。播放列表缓存时间过长,会使边缘或源站不断返回旧序列;缓存时间过短,则会增加美国服务器的请求量。
点播播放列表按照是否版本化处理
未版本化的点播播放列表仍可能被原路径更新,因此可以使用相对短的缓存:
server {
listen 80;
server_name video.example.test;
location ~* ^/hls/vod/.*\.(m3u8|mpd)$ {
proxy_pass http://video_origin;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache manifest_cache;
proxy_cache_methods GET HEAD;
proxy_cache_key "$scheme|$host|$request_uri";
proxy_cache_valid 200 10m;
proxy_cache_lock on;
proxy_no_cache $http_authorization $upstream_http_set_cookie;
proxy_hide_header Cache-Control;
add_header Cache-Control "public, max-age=60, s-maxage=600" always;
add_header X-Object-Cache $upstream_cache_status always;
}
}
如果播放列表路径中包含不可变版本号,并且发布新版本时不会覆盖旧播放列表,则可以把 s-maxage 提高到数小时甚至更长,同时在发布新版本时更新页面引用的新 URL。对于稳定 URL 的播放列表,不建议直接套用一年缓存。
视频切片和版本化文件采用长缓存
直播切片的 URL 通常不会重复使用,可以缓存几十秒到几分钟;带版本号的点播切片则可以长期缓存。下面的配置展示了两种情况:
server {
listen 80;
server_name video.example.test;
location ~* ^/hls/live/.*\.(ts|m4s)$ {
proxy_pass http://video_origin;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache media_cache;
proxy_cache_methods GET HEAD;
proxy_cache_key "$scheme|$host|$request_uri";
proxy_cache_valid 200 60s;
proxy_cache_lock on;
proxy_no_cache $http_authorization $upstream_http_set_cookie;
proxy_hide_header Cache-Control;
add_header Cache-Control "public, max-age=20, s-maxage=60" always;
add_header X-Object-Cache $upstream_cache_status always;
}
location ~* ^/hls/vod/.*\.(ts|m4s|mp4)$ {
proxy_pass http://video_origin;
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_cache media_cache;
proxy_cache_methods GET HEAD;
proxy_cache_key "$scheme|$host|$request_uri";
proxy_cache_valid 200 30d;
proxy_cache_lock on;
proxy_no_cache $http_authorization $upstream_http_set_cookie;
proxy_hide_header Cache-Control;
add_header Cache-Control "public, max-age=31536000, s-maxage=31536000, immutable" always;
add_header X-Object-Cache $upstream_cache_status always;
}
}
点播对象只有在 URL 版本化、内容不会被原地替换,并且没有依赖用户权限的前提下,才适合使用 immutable 和一年级别的客户端缓存。若仍然使用固定文件名,就应把 30d 和一年缓存改为较短周期,例如 5~30 分钟,并在更新后主动清理对应对象。
对于支持 Range 请求的 MP4 文件,还要检查源站返回的是 200 还是 206。不要在没有验证的情况下盲目增加对 206 响应的缓存规则,否则可能出现分段响应不完整或缓存对象异常。HLS、DASH 的独立切片通常更容易按照完整对象进行缓存。
查询参数和边缘缓存应该怎样配合
配置中的 $request_uri 包含路径和查询参数,适合保留签名参数,避免不同签名请求被错误合并。但它也会带来一个问题:同一个视频加上不同的统计参数后,会产生多个缓存对象。
可以按以下顺序判断:
- 查询参数会改变内容吗?例如清晰度、语言、版本号会改变内容,必须保留在缓存键中。
- 查询参数只用于权限校验吗?未确认鉴权已经在缓存前完成时,默认不要做共享缓存。
- 查询参数只是统计来源吗?确认不会影响权限和响应内容后,才考虑在边缘层或应用层做参数白名单归一化。
- 响应是否会随
Accept-Language、设备类型或其他请求头变化?如果会变化,就要正确使用Vary,或者拆分缓存键;无法确认时直接绕过共享缓存更安全。
如果美国服务器前面有边缘缓存,响应头中的 s-maxage 应作为边缘缓存的主要参考,max-age 则主要影响浏览器。一个公共页面可以这样返回:
Cache-Control: public, max-age=10, s-maxage=30, stale-while-revalidate=10
含义是浏览器最多使用 10 秒的副本,边缘缓存可以使用 30 秒;重新获取时允许在后台更新的短时间内返回旧副本。对于登录页面则应返回:
Cache-Control: private, no-store
不要只依赖 Nginx 的 proxy_cache_valid。它控制的是 Nginx 这一层的缓存时间,不等于浏览器或边缘缓存的失效时间。多层缓存应先确定整体新鲜度预算,再分别设置每一层的值。页面要求最多陈旧 60 秒时,浏览器可以设 10 秒,边缘设 30 秒,源站本地缓存设 20~30 秒,并配合重新验证;如果内层缓存长期保留旧页面,外层即使重新回源,也可能再次拿到旧内容。
发布、失效与回滚应该按什么顺序执行
内容发布时,顺序比单纯清缓存更重要。推荐按照以下流程执行:

- 先将新版本视频切片、封面和字幕上传到新的版本路径。
- 直接请求新对象,确认返回状态为
200,长度和媒体类型正确。 - 生成引用新对象路径的播放列表。
- 先让美国服务器源站能够返回新播放列表,再更新视频详情页或首页引用。
- 对页面和未版本化播放列表执行精确 URL 清理,不要一开始就清空所有媒体缓存。
- 观察旧页面命中率、源站请求量和播放器错误,再决定是否清理旧对象。
如果发布失败,回滚时不要立即删除旧版本对象。只要旧版本对象仍然存在,就可以把页面和播放列表重新指向上一版本。对于版本化对象,回滚通常只是恢复引用关系,不需要重新上传视频。
如果页面使用固定 URL,回滚后仍看到旧内容,先检查页面缓存是否尚未过期;如果是内容错误或权限错误,不能只等待 TTL,应立即清理受影响的页面键,并暂时将页面缓存切换为关闭或极短 TTL。
修改 Nginx 配置前,应先备份当前配置。以下命令适用于使用 systemd 的 Ubuntu 或 Debian 环境,执行前确认 Nginx 服务名和配置路径:
sudo cp -a /etc/nginx/nginx.conf \
"/etc/nginx/nginx.conf.bak.$(date +%Y%m%d-%H%M%S)"
sudo nginx -t
sudo systemctl reload nginx
如果测试失败或 reload 后出现异常,恢复备份并重新检查:
sudo cp -a /etc/nginx/nginx.conf.bak.YYYYMMDD-HHMMSS \
/etc/nginx/nginx.conf
sudo nginx -t
sudo systemctl reload nginx
不要把 rm -rf /var/cache/nginx/* 作为第一种失效手段。它会一次性清掉所有本地缓存,可能在短时间内让大量请求回源,增加应用和美国服务器的压力。如果确实需要清理本地缓存,应先确认备份、维护窗口、缓存目录和 Nginx 工作用户,再采用改名保留的方式,而不是直接删除;清理范围也应优先限定到页面缓存或指定对象。
怎样验证缓存规则真的生效
首先验证 Nginx 配置和缓存目录:
nginx -V 2>&1 | head -n 2
sudo nginx -t
df -h /var/cache/nginx
如果缓存目录是新建的,还要确认 Nginx 工作用户具有写入权限。Ubuntu 或 Debian 常见工作用户为 www-data,但应以实际进程为准:
ps -eo user,comm,args | grep '[n]ginx'
然后连续请求同一个公共页面:
curl -sS -D - -o /dev/null \
'http://video.example.test/video/123'
curl -sS -D - -o /dev/null \
'http://video.example.test/video/123'
在配置了诊断响应头的情况下,常见结果应类似:
第一次:X-Page-Cache: MISS
第二次:X-Page-Cache: HIT
过期后:X-Page-Cache: EXPIRED
如果第二次仍然是 MISS,依次检查:
- 请求是否带有
session_id或 Authorization; - 页面响应是否包含
Set-Cookie; - 当前请求是否实际进入了预期的
location; - 查询参数是否每次都在变化;
proxy_cache_path目录是否可写;- reload 后配置是否真的加载成功。
再验证直播播放列表:
curl -sS -D - -o /dev/null \
'http://video.example.test/hls/live/channel-1.m3u8'
sleep 4
curl -sS -D - -o /dev/null \
'http://video.example.test/hls/live/channel-1.m3u8'
连续请求在 3 秒内出现命中是正常现象;等待超过失效时间后,应重新从上游获取或进行重新验证。若播放列表长时间不变,检查的是播放列表 TTL、源站是否真的生成了新序列,以及边缘缓存是否拥有更长的 s-maxage,而不是先清理所有视频切片。
最后验证点播对象是否按版本隔离:
curl -sS -D - -o /dev/null \
'http://video.example.test/hls/vod/movie-202610/v7/seg-00001.m4s'
curl -sS -D - -o /dev/null \
'http://video.example.test/hls/vod/movie-202610/v8/seg-00001.m4s'
v7 和 v8 应被视为两个独立对象。若更新 v8 后播放器仍请求 v7,问题在页面或播放列表引用;若播放器已经请求 v8 却收到旧字节,则应检查源站文件、缓存键和是否存在原路径覆盖。
部署时可以先选择一组公共页面、一个直播频道和一个版本化点播对象进行小范围验证:页面用秒级到分钟级缓存,直播播放列表用 1~5 秒缓存,版本化切片用长缓存,登录请求完全绕过。确认命中状态、内容版本和回滚动作都符合预期后,再扩大到全部视频路径。你现在需要先确认的行动问题是:站点中的每个 URL,究竟属于“会被原地更新的内容”,还是“可以通过新版本 URL 永不覆盖的对象”?