Next.js 内存泄漏:Fetch + Standalone 模式——2年未修复
Next.js 修补全局 fetch 并添加一个在每次请求时泄漏内存的缓存层。在 Docker/K8s 中,这会导致每隔几小时发生 OOM 崩溃。该 bug 自 Next.js 14 以来就存在,在 16.2.x 中仍未解决。
TL;DR
Next.js 修补了全局 fetch 并添加了一个缓存层,该层在响应数据应该被释放后仍持有引用。每次 fetch 调用都会添加永远不会被 GC 回收的内存。在 Docker/Kubernetes 中,这会导致每隔几小时就发生 OOM 崩溃。该 bug 自 Next.js 14(2024年4月)以来就存在,在 16.2.x(2026年3月)中仍未解决。在 Vercel 上由于短暂的 serverless 函数,问题不会显现。
正常 fetch 的工作方式
Request → fetch → got data → response to user → GC cleans up → memory freeNext.js 中 fetch 的工作方式
Next.js 拦截全局 fetch 并用自己的缓存/追踪层包装它:
Request → Next.js fetch wrapper → creates:
- Performance entry (speed tracking) ← not cleaned
- Request metadata object ← not cleaned
- Internal promise wrapper ← not cleaned
- Cache lookup entry ← not cleaned
- Response body (for unique URL) ← not cleaned
→ Response to user
→ GC sees objects are still referenced → doesn't touch them
→ Memory grows indefinitely生产环境中发生的情况
Self-hosted (Docker/K8s):
Request 1: fetch → +100KB → RAM: 100KB
Request 2: fetch → +100KB → RAM: 200KB
Request 1000: fetch → +100KB → RAM: 100MB
Request 50,000: fetch → +100KB → RAM: 5GB → OOM Kill 💀
→ Kubernetes restarts the pod → cycle repeats
Vercel (Serverless):
Request 1: [start] → fetch → +100KB → [process dies] → 0 KB ✅
Request 2: [start] → fetch → +100KB → [process dies] → 0 KB ✅
→ Memory never accumulates受影响的版本
Next.js — 所有带 App Router 的版本
| Version | Issue | Date |
|---|---|---|
| 14.2.x | #64212 | April 2024 |
| 14.x-15.x | #68578 | August 2024 |
| 14.3.0-canary | #79588 | May 2025 |
| 16.0.1 | #85914 | November 2025 |
| 16.1.0 | #88603 | January 2026 |
| 16.0.10 | #90433 | February 2026 |
| 16.2.0-canary.51 | Confirmed in #90433 comments | March 2026 |
Node.js
在 Node.js 20、22、24、25 上测试——全部泄漏。
无效的方法
| Attempt | Result |
|---|---|
cacheMaxMemorySize: 0 | Doesn't help — leak is not from this cache |
| Disable image optimization | Doesn't help |
--max-old-space-size=6144 | Just crashes slower |
Read response via .json() / .text() | Doesn't help |
| Remove React fetch patching (canary.45) | Doesn't help |
| Upgrade to latest version | Doesn't help (leaks on 16.2.0-canary.51 too) |
有效的方法(变通方案)
| Workaround | Why it helps |
|---|---|
Replace fetch with axios | Bypasses Next.js wrapper |
Replace fetch with node-fetch | Same reason |
| Downgrade Node.js to 20.15.1 | Older undici has fewer leaks (for some) |
Docker image node:20-alpine3.21 | Helps in some cases |
| Deploy to Lambda (SST + OpenNext) | Ephemeral functions — memory doesn't accumulate |
| Deploy to Vercel | Same — serverless |
axios 变通方案的限制
你自己的 API 调用可以替换为 axios。但 Next.js 内部使用被修补的 fetch 用于:
- ISR (Incremental Static Regeneration)
revalidatePath/revalidateTag- Server Components 数据获取与去重
use cache(Next.js 16)
即使代码中没有一个 fetch——Next.js 仍然在内部使用它。
Vercel 为什么不修复
商业逻辑
Vercel 是一家靠托管 Next.js 赚钱的公司。在他们的平台上问题不会显现(serverless = 短暂的)。该 bug 只影响自托管(Docker、K8s、VPS)——那些不付费给 Vercel 的人。
官方立场
Tim Neutkens(Vercel 维护者)分析了这个问题并宣称这是 undici(Node.js fetch 库)的问题,而非 Next.js。Issue #90433 被关闭了。尽管:
- 在相同的 Node.js 上,axios 和 node-fetch 没有泄漏
- 泄漏仅在 fetch 通过 Next.js 包装器时出现
- 该 bug 已开放 2 年未修复
优先级
这 2 年里 Next.js 团队发布了:
- Turbopack(构建速度快 2-5 倍)——营销优势
- Cache Components /
use cache——减轻 Vercel 服务器负载 proxy.ts替代 middleware——简化 Vercel 的 edge 部署- DevTools MCP——AI 热潮
自托管中的内存泄漏?不是优先事项。
解决方案:AWS Lambda(SST + OpenNext)
这是什么
OpenNext 是一个开源适配器,将 Next.js 构建转换为 AWS Lambda 格式。SST 是一个自动化基础设施的框架。
架构
Next.js build
→ OpenNext
→ AWS Lambda (SSR, API routes)
→ S3 (static, assets)
→ CloudFront (CDN)
→ SQS + DynamoDB (ISR revalidation)为什么这能解决内存泄漏
Lambda 函数处理请求并在 5-15 分钟不活动后被回收。内存来不及积累。
部署
npx sst@latest init
npx sst deploy --stage production比较
| Vercel | AWS Lambda (SST) | Docker self-hosted | |
|---|---|---|---|
| Memory leak | Not felt | Not felt | Critical |
| Cold starts | Yes | Yes (~200-500ms) | No |
| Price (~medium traffic) | $20-150/mo | $5-30/mo | $5-50/mo |
| Control | Minimal | Full | Full |
| ISR/Revalidation | Works | Works (SQS) | Works (with leak) |
| Vendor lock-in | Vercel | AWS | None |
Lambda 注意事项
- 冷启动——第一个请求较慢(~200-500ms)
- 安全性——启用 OAC(Origin Access Control),否则 Lambda URL 是公开的
- OpenNext——社区项目,非 Vercel 官方。Next.js 新功能可能会出问题
- 钱包攻击——在 DDoS 期间,Lambda 自动扩展可能导致高额账单
为什么真正的修复不现实
1. 架构问题
泄漏不是偶然的 bug,而是设计决策的结果:Next.js 拦截全局 fetch 并在其上添加缓存/追踪。要修复它,需要重新设计 App Router 与 fetch 的交互方式。这涉及 ISR、revalidation、data cache、request deduplication——框架的核心。
2. 利益冲突
Vercel 没有动力去修复不影响其平台的问题。自托管与其业务竞争。自托管问题越多——迁移到 Vercel 的人越多。
3. 推卸责任
官方立场是 "这是 undici 的问题,不是我们的"。在这改变之前——他们不会着手修复。
4. 没有社区修复
Next.js 的 AGPL-3.0 许可证允许 fork,但代码库庞大且与 Vercel 基础设施紧密耦合。修复 fetch 包装器的社区 PR 需要深入了解内部架构并获得维护者的批准——而他们已经关闭了 issue。
结论
- 如果在 Vercel 上——没问题,无需操作
- 如果自托管且需要 serverless——在 AWS Lambda 上使用 SST + OpenNext
- 如果自托管 Docker——尽可能用 axios 替换 fetch,监控 RAM,设置自动 pod 重启
- 如果开始新项目——考虑 SvelteKit 或 Nuxt 作为没有此问题的替代方案
来源
- Issue #64212 — Memory Leak with global fetch (April 2024)
- Issue #68578 — Possible memory leak in Fetch API (August 2024)
- Issue #85914 — Memory Leak with fetch + standalone (November 2025)
- Issue #90433 — OOM in 16.0.10 (February 2026)
- Discussion #88603 — OOM in Docker/K8s (January 2026)
- Discussion #88078 — cacheMaxMemorySize behavior
- OpenNext
- SST — Next.js on AWS
- Secret knowledge to self-host Next.js
- Next.js Memory Usage Guide