Nuxt 3 缓存别把登录态混进去:我把页面拆成匿名壳和用户态片段
一次游客看到别人昵称的 BUG,让我意识到 SSR 缓存不能只按 URL 存。于是把公共内容做成匿名壳,把用户态拆到组件级请求,用 Redis 键分组隔离。

作者
ZHOUYI
线上有用户反馈:未登录打开首页,居然看到了“欢迎你,某某”。第一反应是见鬼了,查下去发现是 SSR 页面缓存把登录用户的 HTML 存了下来,下一个游客请求同一个 URL 时直接命中。问题根因很简单:缓存键只看了路径和参数,没区分访问者身份。
先把缓存键从 URL 改成匿名分组
我一开始想按 userId 拆缓存,但这样命中率会掉得很厉害。更稳的做法是分成两层:匿名公共内容可以共享缓存;登录态内容要么不缓存,要么只按用户短缓存。判断依据是请求里有没有会话 Cookie,但 Redis key 里不直接放敏感信息,只放 anon 或 auth 这种分组。
const hasSession = Boolean(cookieHeader?.includes('sid='))
const group = hasSession ? 'auth' : 'anon'
const key = `page:${pathname}:${group}`
if (group === 'auth') {
return renderNoStore()
}
const html = await redis.get(key)
if (html) return html用户态不进壳,组件级再取
页面级 HTML 只保留公共内容:文章正文、列表、SEO 标题、结构化描述。导航栏昵称、收藏状态、未读消息这些,全部放到组件级请求里再取。这样游客拿到的是可缓存的匿名壳,登录用户进来后,前端组件再补上私有片段,避免整页 HTML 被串号。
- 公共内容:按 URL + anon 分组缓存
- 用户态:按会话短缓存,或直接 no-store
- 组件级请求单独控制缓存,避免被页面缓存吞掉
踩坑:Cookie 分组要和缓存策略一致
最开始我只在服务端读了 Cookie 分组,但上层代理和 CDN 并不知道这个差异,导致仍有可能缓存错内容。后来我把规则统一:只要命中 auth 分组,响应就明确不缓存;只有 anon 分组才允许进入 Redis 或 CDN。涉及具体响应头和缓存策略,建议以官方文档为准。
“SSR 缓存最怕的不是慢,而是把“你的页面”错发给“他”。”
改造后,首页和文章页依旧能吃满 Redis 缓存,但登录昵称、个人状态不再进入公共 HTML。这个方案不复杂,核心就三件事:缓存键区分匿名与登录、页面壳只放公共内容、用户态交给组件级请求。遇到类似 BUG,先问自己:这页有没有私有数据?缓存键有没有区分身份?用户态是不是被提前渲染进去了?