Skip to content
返回

Next.js 静态导出时的路由片段(`/route.txt?_rsc=xxxxxx`)缓存问题

我使用 Next.js 开发了一个网站,并且使用了静态导出。在部署到静态服务器之后,我发现首页跳转很慢。

我的网站会在首页 / 时,通过 navigator.language 判断是跳转到 /zh-cn 还是 /en,然后,这俩页面是需要登录才能访问的,所以没有登录的时候,会再次跳转到 /zh-cn/signin?next=/

也就是说,当我在浏览器地址栏输入 / 时,会进行两次跳转:

  1. 先从 / 跳转到 /zh-cn
  2. 再从 /zh-cn 跳转到 /zh-cn/signin?next=%2F%2F 也就是 /

而我发现,这两次跳转居然要 1 秒左右。

在检查了网络后,我发现第一次跳转时,Next.js 会发起一次请求 /zh-cn.txt?_rsc=rxx9e,第二次跳转时会再发起请求 /zh-cn/signin.txt?next=%2F&_rsc=1xsn2,而这两次请求就是导致跳转时间变长的元凶。

尝试缓存 txt 文件

实际上,在静态导出的情况下,无论 _rsc 参数的值是什么,这些路由片段 txt 文件的内容都是一样的;但是,在下一次修改了代码并重新 build 之后,这些 txt 文件的内容可能会发生变化。

于是,我给 Netlify 添加了一条缓存规则:

[[headers]]
  # 匹配以 .txt 结尾且包含 _rsc 参数的 URL
  for = "/*.txt"
  query = {_rsc = ":any"}
  [headers.values]
    Cache-Control = "public, max-age=31536000, immutable"

但没过多久我就发现:对同一个 txt 文件的请求携带的 _rsc 的值是会变的!

会变化的 _rsc

经过测试,我发现:

我猜测 _rsc 的变化是因为带有 next 这个 search param 导致的,但是在这之前我以为是 URL 里的 zh-CN 的大小写导致的(可能也有关),这里记录一下。

Netlify 中的 URL 大小写问题

我一开始在 URL 中使用的 locale 是大小写混合的,例如 /zh-CN/zh-CN/signin,但是,我发现当我刷新网页的时候,Netlify 会自动 301 到小写的网址 /zh-cn/zh-cn/signin,且这一操作无法禁用,见 How to disable “Uppercase URLs are redirected to Lowercase URLs” feature?

然后我将代码、文件名里用了大小写混合的 locale 的地方全都统一改成了小写的 zh-cn 这种形式,又发现我在地址栏输入 /zh-cn 时,网络里还是有 301 到 /zh-cn 的 document 请求。经过确认,发现是因为我之前输入的网址都是 /zh-CN 形式,所以我现在即使输入了小写的 /zh-cn,浏览器地址栏实际上请求的是曾经的历史记录里的 /zh-CN。于是,我又在地址栏的下拉框里删除了所有 /zh-CN 形式的历史记录,浏览器才开始准确请求 /zh-cn

但是,_rsc 变化的问题依然存在,所以我才意识到可能还有别的原因会引起 _rsc 变化。

验证猜想

为了验证是否真的是 search param 导致的 _rsc 变化,我将 next 参数改为放在了 hash 里,即以前是 /zh-cn/signin?next=%2F,现在改成 zh-cn/signin#next=%2F,然后再试验上面两种跳转情况,结果发现,跟使用 search param 时是一样的行为大致一样,两点不同:

200 与 304

靠 _rsc 来对 txt 进行缓存是不大可能了,我又将目光转到了 txt 文件的 HTTP 响应码上。理论上,同一次 build 的 txt 文件内容是一样的,那么应该总是返回 304 才对。

我研究了一会儿 signin.txt 的响应头,发现里面既有 Netlify CDN 的相关内容,也有 Cloudflare CDN 的相关内容。我突然想到,在 Netlify 里好像看到过,说是 Netlify 本身有 CDN 功能,所以不应该再套别的 CDN,例如 Cloudflare 的 CDN,而我确实在 Cloudflae DNS 里开启了“代理”选项。

我尝试在 Cloudflare DNS 里关闭了代理选项,即变成了“仅 DNS”,然后再刷新网页——所有 txt 都始终返回 304 了,无论 _rsc 值是什么。

到这里,问题其实已经解决了一大半了,而 _rsc 不恒定导致没法根据 _rsc 来设置一年缓存,所以作罢。

结论

相关链接:


分享这篇文章:

上一篇
从 Netlify 迁移到了 Cloudflare Workers ——我居然“浪费”了这么多钱
下一篇
WXT 尝试