Next.js Memory Leak: Fetch + Standalone Mode — 2 года без фикса
Next.js патчит глобальный fetch и добавляет кеш-слой, который течёт на каждом запросе. В Docker/K8s это приводит к OOM crash каждые несколько часов. Баг существует с Next.js 14 и до сих пор не решён в 16.2.x.
TL;DR
Next.js патчит глобальный fetch и добавляет кеш-слой, который держит ссылки на response data после того как они должны были быть освобождены. Каждый вызов fetch добавляет память, которая никогда не возвращается GC. В Docker/Kubernetes это приводит к OOM crash каждые несколько часов. Баг существует с Next.js 14 (апрель 2024) и до сих пор не решён в 16.2.x (март 2026). На Vercel проблема не проявляется из-за ephemeral serverless функций.
Как работает нормальный fetch
Request → fetch → got data → response to user → GC cleans up → memory freeКак работает fetch в Next.js
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) |
Что работает (workarounds)
| 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 |
Ограничение workaround с axios
Свои API-запросы можно заменить на axios. Но Next.js внутренне использует пропатченный fetch для:
- ISR (Incremental Static Regeneration)
revalidatePath/revalidateTag- Server Components data fetching с дедупликацией
use cache(Next.js 16)
То есть даже без единого fetch в своём коде — Next.js всё равно использует его под капотом.
Почему Vercel не фиксит
Бизнес-логика
Vercel — компания, которая зарабатывает на хостинге Next.js. На их платформе проблема не проявляется (serverless = ephemeral). Баг затрагивает только self-hosted (Docker, K8s, VPS) — тех, кто не платит Vercel.
Официальная позиция
Tim Neutkens (мейнтейнер Vercel) провёл анализ и заявил что это проблема undici (Node.js fetch библиотека), а не Next.js. Issue #90433 закрыли. При том что:
- axios и node-fetch на том же Node.js работают без утечек
- Утечка появляется только когда fetch проходит через Next.js обёртку
- Баг открыт 2 года без фикса
Приоритеты
За эти 2 года команда Next.js выпустила:
- Turbopack (2-5x быстрее билды) — маркетинговое преимущество
- Cache Components /
use cache— уменьшает нагрузку на серверы Vercel proxy.tsвместо middleware — упрощает edge-деплой на Vercel- DevTools MCP — AI-хайп
Memory leak в self-hosted? Не в приоритетах.
Решение: AWS Lambda (SST + OpenNext)
Что это
OpenNext — open-source адаптер, который превращает Next.js build в формат для AWS Lambda. SST — фреймворк, автоматизирующий инфраструктуру.
Архитектура
Next.js build
→ OpenNext
→ AWS Lambda (SSR, API routes)
→ S3 (static, assets)
→ CloudFront (CDN)
→ SQS + DynamoDB (ISR revalidation)Почему это решает memory leak
Lambda функция обрабатывает запросы и recycled через 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
- Cold starts — первый запрос медленнее (~200-500ms)
- Безопасность — включить OAC (Origin Access Control), иначе Lambda URL публичный
- OpenNext — community проект, не официальный Vercel. Новые фичи Next.js могут ломаться
- Wallet attack — при DDoS автоскейлинг Lambda может привести к большому счёту
Почему исправить нереально
1. Архитектурная проблема
Утечка — не случайный баг, а следствие дизайн-решения: Next.js перехватывает глобальный fetch и добавляет кеш/трекинг поверх. Чтобы пофиксить — нужно переделать как App Router взаимодействует с fetch. Это затрагивает ISR, revalidation, data cache, request deduplication — ядро фреймворка.
2. Конфликт интересов
Vercel не мотивирован фиксить то, что не затрагивает их платформу. Self-hosted конкурирует с их бизнесом. Чем больше проблем в self-hosted — тем больше людей мигрируют на Vercel.
3. Blame shifting
Официальная позиция — "это undici, не мы". Пока она не изменится — над фиксом работать не будут.
4. Нет community fix
AGPL-3.0 лицензия Next.js позволяет форки, но кодовая база огромна и тесно связана с инфраструктурой Vercel. Community PR с фиксом fetch обёртки потребовал бы глубокого понимания внутренней архитектуры и одобрения мейнтейнеров — которые уже закрыли issue.
Выводы
- Если на Vercel — проблемы нет, ничего делать не нужно
- Если self-hosted и нужен serverless — SST + OpenNext на AWS Lambda
- Если self-hosted Docker — заменить fetch на axios где возможно, мониторить RAM, настроить автоматический рестарт подов
- Если начинаешь новый проект — рассмотреть 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