Skip to main content
← 返回博客
Next.jsMemory LeakDockerAWS LambdaNode.js

Next.js 内存泄漏:Fetch + Standalone 模式——2年未修复

Next.js 修补全局 fetch 并添加一个在每次请求时泄漏内存的缓存层。在 Docker/K8s 中,这会导致每隔几小时发生 OOM 崩溃。该 bug 自 Next.js 14 以来就存在,在 16.2.x 中仍未解决。

发布于 2026年3月19日10 分钟阅读

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 free

Next.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 的版本

VersionIssueDate
14.2.x#64212April 2024
14.x-15.x#68578August 2024
14.3.0-canary#79588May 2025
16.0.1#85914November 2025
16.1.0#88603January 2026
16.0.10#90433February 2026
16.2.0-canary.51Confirmed in #90433 commentsMarch 2026

Node.js

在 Node.js 20、22、24、25 上测试——全部泄漏。

无效的方法

AttemptResult
cacheMaxMemorySize: 0Doesn't help — leak is not from this cache
Disable image optimizationDoesn't help
--max-old-space-size=6144Just crashes slower
Read response via .json() / .text()Doesn't help
Remove React fetch patching (canary.45)Doesn't help
Upgrade to latest versionDoesn't help (leaks on 16.2.0-canary.51 too)

有效的方法(变通方案)

WorkaroundWhy it helps
Replace fetch with axiosBypasses Next.js wrapper
Replace fetch with node-fetchSame reason
Downgrade Node.js to 20.15.1Older undici has fewer leaks (for some)
Docker image node:20-alpine3.21Helps in some cases
Deploy to Lambda (SST + OpenNext)Ephemeral functions — memory doesn't accumulate
Deploy to VercelSame — 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

比较

VercelAWS Lambda (SST)Docker self-hosted
Memory leakNot feltNot feltCritical
Cold startsYesYes (~200-500ms)No
Price (~medium traffic)$20-150/mo$5-30/mo$5-50/mo
ControlMinimalFullFull
ISR/RevalidationWorksWorks (SQS)Works (with leak)
Vendor lock-inVercelAWSNone

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。

结论

  1. 如果在 Vercel 上——没问题,无需操作
  2. 如果自托管且需要 serverless——在 AWS Lambda 上使用 SST + OpenNext
  3. 如果自托管 Docker——尽可能用 axios 替换 fetch,监控 RAM,设置自动 pod 重启
  4. 如果开始新项目——考虑 SvelteKit 或 Nuxt 作为没有此问题的替代方案

来源