神族九帝's blog 神族九帝's blog
首页
  • 神卡套餐 (opens new window)
  • 神族九帝 (opens new window)
  • 网盘资源 (opens new window)
  • 今日热点 (opens new window)
  • 在线PS (opens new window)
  • IT工具 (opens new window)
  • FC游戏 (opens new window)
  • 在线壁纸 (opens new window)
  • AI团队搭建总结
  • 面试突击
  • 复习指导
  • HTML
  • CSS
  • JavaScript
  • 设计模式
  • 浏览器
  • 手写系列
  • Vue
  • Webpack
  • Http
  • 前端优化
  • 项目
  • 面试真题
  • 算法
  • 精选文章
  • 八股文
  • 前端工程化
  • 一面
  • 工作笔记
  • 前端基础建设与架构 30 讲
  • vue2源码学习
  • 剖析vuejs内部运行机制
  • TypeScript 入门实战笔记
  • vue3源码学习
  • 2周刷完100道前端优质面试真题
  • 思维导图
  • npm发包
  • 重学node
  • 前端性能优化方法与实战
  • webpack原理与实战
  • webGl
  • 前端优化
  • Web3
  • React
  • 更多
  • 未来要做的事
  • Stirling-PDF
  • ComfyUI
  • 宝塔面板+青龙面板
  • 安卓手机当服务器使用
  • 京东自动评价代码
  • 搭建x-ui免流服务器(已失效)
  • 海外联盟
  • 好玩的docker
  • 收藏夹
  • 更多
GitHub (opens new window)

神族九帝,永不言弃

首页
  • 神卡套餐 (opens new window)
  • 神族九帝 (opens new window)
  • 网盘资源 (opens new window)
  • 今日热点 (opens new window)
  • 在线PS (opens new window)
  • IT工具 (opens new window)
  • FC游戏 (opens new window)
  • 在线壁纸 (opens new window)
  • AI团队搭建总结
  • 面试突击
  • 复习指导
  • HTML
  • CSS
  • JavaScript
  • 设计模式
  • 浏览器
  • 手写系列
  • Vue
  • Webpack
  • Http
  • 前端优化
  • 项目
  • 面试真题
  • 算法
  • 精选文章
  • 八股文
  • 前端工程化
  • 一面
  • 工作笔记
  • 前端基础建设与架构 30 讲
  • vue2源码学习
  • 剖析vuejs内部运行机制
  • TypeScript 入门实战笔记
  • vue3源码学习
  • 2周刷完100道前端优质面试真题
  • 思维导图
  • npm发包
  • 重学node
  • 前端性能优化方法与实战
  • webpack原理与实战
  • webGl
  • 前端优化
  • Web3
  • React
  • 更多
  • 未来要做的事
  • Stirling-PDF
  • ComfyUI
  • 宝塔面板+青龙面板
  • 安卓手机当服务器使用
  • 京东自动评价代码
  • 搭建x-ui免流服务器(已失效)
  • 海外联盟
  • 好玩的docker
  • 收藏夹
  • 更多
GitHub (opens new window)
  • 工作笔记

    • 国际化多语言一键替换,在线翻译
    • 虚拟列表
    • vscode插件开发
    • Code Review
    • 如果AI设计的界面不好看,可以试试下面的提示词
    • git合并冲突
    • isr-cache-debug
      • 问题现象
      • 排查过程
        • 阶段一:怀疑 Next.js ISR 缓存头
        • 阶段二:从 Next.js 层面禁用 ISR
        • 阶段三:定位真正的缓存层
        • 阶段四:最终修复
        • 阶段五:代码清理
      • 架构分析
      • 经验总结
  • 前端基础建设与架构 30 讲

  • vue2源码学习

  • 剖析vuejs内部运行机制

  • TypeScript 入门实战笔记

  • vue3源码学习

  • 2周刷完100道前端优质面试真题

  • 思维导图

  • npm发包

  • 重学node

  • 前端性能优化方法与实战

  • webpack原理与实战

  • webGl

  • 前端优化

  • Web3

  • React

  • 更多

  • 笔记
  • 工作笔记
wu529778790
2026-05-28

isr-cache-debug

# 问题现象

通过域名访问 telegramdigest.shenzjd.com 显示旧版 4 卡片布局,但通过 IP 直接访问显示新版 2 卡片布局。Vercel 部署正常,Docker 自部署有问题。

# 排查过程

# 阶段一:怀疑 Next.js ISR 缓存头

观察到 Next.js ISR 生成的响应带有 cache-control: s-maxage=31536000(缓存 1 年),认为 Cloudflare CDN 据此缓存了旧版本 HTML。

尝试方案(全部失败):

提交 方法 结果
924700a 拦截 ServerResponse.prototype.setHeader / writeHead 生产环境无效
384fd34 拦截 net.Socket.prototype.write 改写原始 HTTP 头 无效
f1f6fee Dockerfile 清除 .next/cache + socket.write 拦截 无效
2bf68f1 拦截 socket.writev() 处理 Node.js 22 批量写入 无效
e2f5c1d 拦截 _storeHeader 改写已序列化的响应头 无效

失败原因: Next.js ISR 在框架内部设置缓存头,优先级高于所有外部拦截。自定义 server 中的 prototype patching 在不同 Node.js 版本和代码路径下不可靠。

# 阶段二:从 Next.js 层面禁用 ISR

提交 方法 结果
89ff60a 在 layout.tsx 加 export const dynamic = 'force-dynamic' ISR 仍然生效
75c880b 添加 middleware.ts 用 NextResponse.next() 设置 no-store 头 ISR 响应头覆盖了 middleware 的头

发现: 构建输出确认所有路由标记为 ƒ (Dynamic),但运行时 ISR 缓存仍然活跃。middleware 的响应头被 ISR 的 s-maxage 覆盖。

# 阶段三:定位真正的缓存层

通过对比测试找到关键线索:

# 带查询参数 → 正确
curl -sI "https://telegramdigest.shenzjd.com/?v=2"
# cache-control: no-store, no-cache, must-revalidate, proxy-revalidate
# x-cache: MISS

# 不带参数 → 缓存
curl -sI "https://telegramdigest.shenzjd.com/"
# cache-control: s-maxage=31536000
# x-cache: HIT
# x-nextjs-cache: HIT

逐个分析响应头:

响应头 来源 含义
cf-cache-status: DYNAMIC Cloudflare 未缓存(Cloudflare 不是问题)
x-nextjs-cache: HIT Next.js ISR ISR 缓存命中
x-cache: HIT OpenResty 代理缓存命中

结论: Cloudflare 完全没有缓存。真正的缓存层是 OpenResty (Nginx) 的 proxy_cache。1Panel 的 OpenResty 配置了代理缓存,看到了 Next.js ISR 发出的 s-maxage=31536000 后将响应缓存了 1 年。

# 阶段四:最终修复

提交 方法 结果
177cd31 middleware 用 NextResponse.rewrite() 添加时间戳参数绕过 ISR 有效
// src/middleware.ts
export function middleware(request: NextRequest) {
  const url = request.nextUrl.clone();
  url.searchParams.set('__nocache', Date.now().toString());
  const response = NextResponse.rewrite(url);
  response.headers.set('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
  return response;
}

原理:

  1. rewrite 内部将 / 改写为 /?__nocache=1234567890,浏览器 URL 不变
  2. Next.js ISR 缓存基于完整 URL(含查询参数),唯一 URL 永远不会命中缓存
  3. 响应头 no-store 防止 OpenResty 再次缓存

服务端操作: 手动删除 OpenResty 磁盘缓存目录后 reload。

# 阶段五:代码清理

今天共产生 12 个调试提交,大部分代码改动无效。清理后:

  • 保留:src/middleware.ts(唯一有效修复)
  • 恢复:src/server.ts(移除所有 prototype patching hack)
  • 恢复:src/app/page.tsx(原始内联仪表盘)
  • 删除:dashboard-page.tsx(调试期间创建的临时文件)
  • 恢复:Dockerfile、next.config.ts、.gitignore

# 架构分析

请求经过三层缓存,每一层都可能导致问题:

浏览器 → Cloudflare CDN → OpenResty (proxy_cache) → Docker/Next.js (ISR)
           cf-cache-status    x-cache              x-nextjs-cache
           = DYNAMIC ✓        = HIT ❌             = HIT ❌
层级 是否缓存 排查方法
Cloudflare 否 (DYNAMIC) cf-cache-status 响应头
OpenResty 是 (HIT) x-cache 响应头
Next.js ISR 是 (HIT) x-nextjs-cache 响应头

# 经验总结

  1. 先看响应头再写代码 — cf-cache-status: DYNAMIC 早就说明 Cloudflare 没问题,不需要任何 header 拦截
  2. 注意 x-cache 头 — 这是 Nginx/OpenResty proxy_cache 的标志头,不是 Next.js 的
  3. 重启不等于清缓存 — Nginx proxy_cache_path 的磁盘缓存文件在重启后仍然存在,需要手动 rm -rf
  4. Node.js 层面的 header 拦截不可靠 — Next.js ISR 在框架内部序列化响应头,外部 prototype patching 在生产环境下几乎不可能可靠工作
  5. Middleware rewrite 是可靠的 ISR 绕过方式 — 通过改变 URL 绕过 ISR 缓存,而不是试图覆盖 ISR 的响应头
编辑 (opens new window)
上次更新: 2026/05/28, 14:51:12
git合并冲突
开篇词 像架构师一样思考,突破技术成长瓶颈

← git合并冲突 开篇词 像架构师一样思考,突破技术成长瓶颈→

最近更新
01
AI团队搭建总结
03-02
02
git合并冲突
01-09
03
如果AI设计的界面不好看,可以试试下面的提示词
10-09
更多文章>
Power by vuepress | Copyright © 2015-2026 神族九帝
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式
×