تسريب ذاكرة Next.js: Fetch + وضع Standalone — سنتان دون إصلاح
يقوم Next.js بترقيع دالة fetch العامة ويضيف طبقة تخزين مؤقت تتسرب منها الذاكرة مع كل طلب. في Docker/K8s يؤدي هذا إلى انهيارات OOM كل بضع ساعات. توجد العلة منذ Next.js 14 ولا تزال دون حل في 16.2.x.
TL;DR
يقوم Next.js بترقيع دالة fetch العامة ويضيف طبقة تخزين مؤقت تحتفظ بمراجع إلى بيانات الاستجابة بعد أن كان ينبغي تحريرها. كل استدعاء لـ fetch يضيف ذاكرة لا تُعاد أبدًا إلى الـ GC. في Docker/Kubernetes يؤدي هذا إلى انهيارات OOM كل بضع ساعات. توجد هذه العلة منذ Next.js 14 (أبريل 2024) ولا تزال دون حل في 16.2.x (مارس 2026). أما على Vercel فلا تظهر المشكلة بفضل دوال 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) |
ما الذي ينفع (حلول التفافية)
| 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 = عابر). العلة تؤثر فقط على الاستضافة الذاتية (Docker وK8s وVPS) — أي على من لا يدفع لـ Vercel.
الموقف الرسمي
قام Tim Neutkens (أحد مشرفي Vercel) بتحليل المشكلة وأعلن أنها مشكلة undici (مكتبة fetch في Node.js) وليست مشكلة Next.js. وأُغلق Issue #90433. وذلك رغم أن:
- axios وnode-fetch يعملان على نفس Node.js دون تسريبات
- التسريب لا يظهر إلا عندما يمر fetch عبر غلاف Next.js
- العلة مفتوحة منذ سنتين دون إصلاح
الأولويات
خلال هاتين السنتين أصدر فريق Next.js ما يلي:
- Turbopack (بناء أسرع بمقدار 2-5x) — ميزة تسويقية
- Cache Components /
use cache— يقلل الحِمل على خوادم Vercel proxy.tsبدلًا من middleware — يبسّط النشر على edge في Vercel- DevTools MCP — ضجّة الذكاء الاصطناعي
تسريب الذاكرة في الاستضافة الذاتية؟ ليس من الأولويات.
الحل: 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
- Cold starts — الطلب الأول أبطأ (~200-500ms)
- الأمان — فعّل OAC (Origin Access Control)، وإلا فإن رابط Lambda يكون علنيًا
- OpenNext — مشروع مجتمعي وليس رسميًا من Vercel. قد تتعطل ميزات Next.js الجديدة
- هجوم المحفظة — أثناء هجوم DDoS قد يؤدي التوسع التلقائي لـ Lambda إلى فاتورة ضخمة
لماذا يُعدّ الإصلاح الحقيقي غير واقعي
1. مشكلة معمارية
التسريب ليس علة عرضية بل نتيجة قرار تصميمي: يعترض Next.js دالة fetch العامة ويضيف فوقها التخزين المؤقت/التتبّع. ولإصلاحه يلزم إعادة تصميم طريقة تفاعل App Router مع fetch. وهذا يمسّ ISR وrevalidation وdata cache وrequest deduplication — أي نواة الإطار.
2. تضارب المصالح
Vercel غير متحفزة لإصلاح ما لا يؤثر على منصتها. الاستضافة الذاتية تنافس عملها التجاري. وكلما زادت المشكلات في الاستضافة الذاتية — زاد عدد من ينتقلون إلى Vercel.
3. تحميل المسؤولية لغيرها
الموقف الرسمي هو «إنها undici، لسنا نحن». وحتى يتغير هذا — لن يعملوا على إصلاح.
4. لا يوجد إصلاح من المجتمع
رخصة AGPL-3.0 الخاصة بـ Next.js تسمح بالتفريعات، لكن قاعدة الشيفرة ضخمة ومترابطة بإحكام مع بنية Vercel التحتية. أي طلب دمج مجتمعي لإصلاح غلاف fetch سيتطلب فهمًا عميقًا للبنية الداخلية وموافقة المشرفين — الذين أغلقوا الـ issue بالفعل.
الخلاصات
- إن كنت على Vercel — لا مشكلة، لا شيء تفعله
- إن كنت على استضافة ذاتية وتحتاج serverless — SST + OpenNext على AWS Lambda
- إن كنت على استضافة ذاتية بـ Docker — استبدل fetch بـ axios حيثما أمكن، وراقب الـ RAM، واضبط إعادة تشغيل تلقائية للـ pods
- إن كنت تبدأ مشروعًا جديدًا — فكّر في 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