上一篇 下一篇 分享链接 返回 返回顶部

美国服务器部署视频站时,页面缓存与视频对象缓存应如何分层设置失效规则?

发布人:Minchunlin 发布时间:2026-10-03 15:28 阅读量:8

美国服务器部署视频站时,页面缓存与视频对象缓存不应使用同一套失效规则。更稳妥的分层方式是:匿名页面采用短时间缓存,并在内容发布、登录状态或个性化条件变化时主动失效;视频切片、封面和可下载文件按照“内容是否会被原路径替换”决定缓存时长,使用版本化路径的对象可以长期缓存,直播播放列表则必须保持较短的失效周期。

如果站点前面还有边缘缓存,建议形成“浏览器—边缘缓存—美国服务器本地缓存—应用或对象存储”的层级;如果没有边缘缓存,则由美国服务器上的 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 包含路径和查询参数,适合保留签名参数,避免不同签名请求被错误合并。但它也会带来一个问题:同一个视频加上不同的统计参数后,会产生多个缓存对象。

可以按以下顺序判断:

  1. 查询参数会改变内容吗?例如清晰度、语言、版本号会改变内容,必须保留在缓存键中。
  2. 查询参数只用于权限校验吗?未确认鉴权已经在缓存前完成时,默认不要做共享缓存。
  3. 查询参数只是统计来源吗?确认不会影响权限和响应内容后,才考虑在边缘层或应用层做参数白名单归一化。
  4. 响应是否会随 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 秒,并配合重新验证;如果内层缓存长期保留旧页面,外层即使重新回源,也可能再次拿到旧内容。

发布、失效与回滚应该按什么顺序执行

内容发布时,顺序比单纯清缓存更重要。推荐按照以下流程执行:

发布、失效与回滚应该按什么顺序执行配图

  1. 先将新版本视频切片、封面和字幕上传到新的版本路径。
  2. 直接请求新对象,确认返回状态为 200,长度和媒体类型正确。
  3. 生成引用新对象路径的播放列表。
  4. 先让美国服务器源站能够返回新播放列表,再更新视频详情页或首页引用。
  5. 对页面和未版本化播放列表执行精确 URL 清理,不要一开始就清空所有媒体缓存。
  6. 观察旧页面命中率、源站请求量和播放器错误,再决定是否清理旧对象。

如果发布失败,回滚时不要立即删除旧版本对象。只要旧版本对象仍然存在,就可以把页面和播放列表重新指向上一版本。对于版本化对象,回滚通常只是恢复引用关系,不需要重新上传视频。

如果页面使用固定 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 永不覆盖的对象”?

目录结构
全文