十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Next.js 的 CDN 缓存为什么没生效,s-maxage 与按需重验证怎么配合

Next.js 的 CDN 缓存为什么没生效,s-maxage 与按需重验证怎么配合 Next.js 的 CDN 缓存为什么没生效s-maxage 与按需重验证怎么配合【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js在 Next.js 前面加一层 CDN 后常见的两个症状是页面在边缘节点上完全没有命中缓存或者调用了revalidateTag()/revalidatePath()之后 CDN 上返回的内容依然不更新。这两种情况的根因不同前者通常出在路由的渲染策略或 CDN 对缓存键的处理上后者则是 CDN 缓存与 Next.js 服务端缓存本来就是两层独立缓存按需重验证只作用于其中一层。这篇文章基于官方文档梳理判断路径如何用响应头定位问题出在哪一层以及s-maxage、stale-while-revalidate与按需重验证应如何配合。适用对象是通过next start、Docker 容器等方式自托管并自行配置 CDN 的场景参考 CDN Caching 与 Self-Hosting。先判断问题出在哪一层读响应头CDN 缓存是否生效第一步不是改 CDN 配置而是看响应头。Next.js 按每条路由的渲染策略设置Cache-Control头来自 CDN Caching路由类型Cache-Control头静态页面无重验证s-maxage31536000一年ISR 页面基于时间的重验证s-maxage{revalidate}, stale-while-revalidate{expire - revalidate}默认expire为一年所以默认会带stale-while-revalidate动态页面不缓存private, no-cache, no-store, max-age0, must-revalidate/_next/static/下的静态资源public, max-age31536000, immutable文件名带内容哈希先按这个表核对你的路由拿到的头如果 ISR/静态路由拿到的是private, no-cache, ...说明该路由被判定为动态渲染CDN 本就不应缓存它——这不是 CDN 配置错了。原因通常是路由里访问了动态 APIcookies、headers等 request-time API。Self-Hosting 文档明确指出页面访问动态 API 时会带Cache-Control: private表示该 HTML 标记为不可缓存只有完全预渲染为静态的页面才会带允许 CDN 缓存的头。如果头是s-maxage...但 CDN 仍不缓存问题转向 CDN 一侧的缓存键处理见下文。判断 Next.js 服务端缓存本身的命中情况可以用两个官方手段来自 ISR 指南的 Troubleshooting 与 Caveats本地跑生产路径next build之后next start这样测试到的就是生产环境的 ISR 行为。在.env里加上下面这行Next.js 服务端控制台会打印 ISR 缓存命中/未命中NEXT_PRIVATE_DEBUG_CACHE1观察响应头x-nextjs-cache。文档给出的取值含义HIT命中缓存、STALE命中缓存并在后台重验证、MISS未命中全新渲染、REVALIDATED经按需重验证后重新生成。用这三个手段可以先把CDN 没缓存和Next.js 服务端缓存没命中区分开再分别处理。按需重验证为什么不会传到 CDN这是内容不更新这个症状的核心机制。CDN Caching的说法很直接revalidateTag()/revalidatePath()只会让 Next.js 的服务端缓存失效CDN 会继续提供它自己缓存的副本直到s-maxageTTL 到期。文档给出的配合模式是两步调用revalidateTag()/revalidatePath()使 Next.js 服务端缓存失效紧接着调用你 CDN 的 purge API清除受影响的缓存键——注意要同时覆盖 HTML 和 RSC 两个变体。revalidateTag的典型写法Server Action 内来自 Revalidatingimport { revalidateTag } from next/cache export async function updateUser(id: string) { // Mutate data revalidateTag(user, max) // Recommended: stale-while-revalidate }第二参数控制陈旧内容在后台生成新内容期间可以继续被提供多久max是最长陈旧窗口语义是 stale-while-revalidate。适合更新可以稍有延迟的内容比如博客或商品目录。如果需求是用户立刻看到自己的改动read-your-own-writes文档区分了updateTag它立即让缓存过期但只能在 Server Actions 中使用。还有一个容易被忽略的层面多实例部署下revalidateTag()的重验证事件默认是本地的——在实例 A 上调用只会让实例 A 的缓存失效其他实例在获知失效事件之前会继续提供旧内容见 How Revalidation Works。跨实例协调需要通过自定义 cache handler 的updateTags()/refreshTags()把失效事件写入共享存储如 Redis。所以排查重验证不生效时要依次问三个问题CDN 有没有 purge其他 Next.js 实例是否共享了 tag 失效状态purge 是否覆盖了 HTML 和 RSC 两个键CDN 侧必须配置对的三件事App Router 的响应会随若干自定义请求头变化Next.js 通过Vary头向 CDN 声明这一点涉及rsc、next-router-state-tree、next-router-prefetch、next-router-segment-prefetch以及拦截路由场景下的next-url。很多 CDN 不配置就不支持VaryNext.js 用_rsc查询参数解决这个问题它是相关请求头值的哈希作为缓存键使用让不同响应变体拿到不同的缓存键。据此CDN Caching把 CDN 侧的行为分为两类必须保留的rsc请求头必须从客户端转发到服务端。它告诉服务端返回 RSC 载荷而非 HTML。如果 CDN 把它剥掉服务端会在客户端路由期望 RSC 数据时返回 HTML破坏客户端导航退化成浏览器整页跳转。Vary头与_rsc参数存在的目的就是防止 CDN 把缓存的 HTML 响应返回给 RSC 请求或反过来。_rsc查询参数必须进入缓存键确保 CDN 不会从缓存键中剥离查询参数——部分 CDN 默认会这么做。当 RSC 请求缺少正确的_rsc值时服务端默认会返回307 重定向指向带正确哈希的 URLCDN 应跟随这个重定向。这个行为可以通过把experimental.validateRSCRequestHeaders设为false关闭在上游能计算哈希的平台上也可以在转发前改写请求带上正确的_rsc省掉一次往返。当next-router-prefetch存在时prefetch 头与_rsc参数都要保留对 prefetch 流程而言_rsc是必需的缓存区分因子。可以安全忽略的降级而非报错非 prefetch 的 RSC 请求缺next-router-state-tree服务端返回完整载荷而非定向的段更新。prefetch 请求缺next-router-segment-prefetch服务端退回更宽泛的 prefetch 载荷。拦截路由缺next-url该路由不再支持拦截用户看到的是目标页面本身而非被拦截页面。另外两条边界proxy.js原 Middleware应在 CDN 缓存之前运行保持对鉴权、重定向、rewrites 的权威。如果部署把它放在 CDN 后面需要为依赖proxy.js决策的路由配置缓存 bypass。How Revalidation Works强调不要给 HTML 和 RSC 响应设置不同的 TTL 或失效策略分别缓存。两者从同一组件树重新生成并存入同一缓存条目如果 CDN 一层给出某次渲染的 HTML、另一层给出另一次渲染的 RSC用户会在客户端导航中看到内容错位。s-maxage 与按需重验证的组合方式把上面的机制落成操作两条路径分别如下。时间驱动路径路由声明 ISRApp Router 下export const revalidate 60见 ISR 指南的最小示例。此时响应带s-maxage{revalidate}, stale-while-revalidate{expire - revalidate}。只要 CDN 遵守这两个指令边缘节点会在s-maxage内直接命中缓存过期后继续提供旧内容并回源触发重新生成。用 Caching and Revalidating (Previous Model) 这套模型时这是唯一的时效控制启用 Cache Components 后时效由cacheLife的 profile如cacheLife(hours)revalidate/expire各对应不同时长见 Revalidating中的 profile 表或自定义对象控制expire可通过cacheLife自定义它会反映到stale-while-revalidate上。按需路径给数据打 tagcacheTag(products)或fetch的next: { tags: [...] }变更后在 Server Action / Route Handler 中调revalidateTag(posts, max)同时调用 CDN purge API 清除受影响路由的 HTML 与 RSC 键。ISR 指南给出的revalidatePath用法适用于不想追踪 tag 的场景use server import { revalidatePath } from next/cache export async function createPost() { // Invalidate the cache for the /posts route revalidatePath(/posts) }注意revalidatePath是失效缓存条目重新生成发生在下一次请求文档建议在可能时优先用 tag 级重验证因为它更精确、避免过度失效。验证组合是否生效按前面的方法跑next buildnext start带NEXT_PRIVATE_DEBUG_CACHE1触发一次重验证后请求该路由观察x-nextjs-cache是否从MISS/STALE变为REVALIDATEDCDN 侧则确认 purge 后边缘节点返回的是新内容。限制与边界多实例 CDN 的完整链路有三层缓存要各自协调CDN 缓存、Next.js 服务端缓存默认在本地文件系统按实例隔离、跨实例 tag 状态。共享缓存需要自定义 cache handlercacheHandler: require.resolve(./cache-handler.js)加cacheMaxMemorySize: 0见 Self-Hosting的 Configuring Caching跨实例 tag 同步需要实现refreshTags()。ISR 仅在 Node.js 运行时默认下受支持Static Export 不支持一条预渲染路由里若有多个不同revalidate频率的fetchISR 采用最低时间任一fetch为revalidate: 0或显式no-store该路由变为动态渲染。按需 ISR 请求不会执行 proxyrewrites 和 proxy 里的逻辑不生效所以要重验证精确路径例如/post/1而非被 rewrite 后的/post-1。当前_rsc方案下next-url即使在静态 prefetch 时也会参与哈希忽略它可能导致缓存未命中CDN Caching说明团队正在推进把缓存影响因子全部收进 URL 路径pathname-based cache keying的方向该方向下 CDN 用 pathname 做缓存键、可安全丢弃查询参数、不再需要理解Vary目前处于 active design 阶段尚不是可用功能。进一步阅读CDN Caching、How Revalidation Works、Self-Hosting、ISR。【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表