كتبت npm run dev (أو ظننت أنك كتبتها 😄)، فتحت المتصفح، فصفعك:
This site can't be reached
localhost refused to connect.
ERR_CONNECTION_REFUSED
أول جدار يصطدم به كل من يشغّل مشروعه الأول محليًا. الخبر الجيد: هذا أصدق خطأ في الويب. معناه حرفيًا: «طرقتُ المنفذ، ولا أحد يستمع خلفه».
حل ERR_CONNECTION_REFUSED خلال دقيقة
لا تبدأ بحذف المتصفح أو تغيير إعدادات الإنترنت. أثبت أولًا هل يوجد برنامج
يستمع على المنفذ الذي فتحته. استبدل 3000 بمنفذ مشروعك:
# macOS / Linux
lsof -nP -iTCP:3000 -sTCP:LISTEN
curl -I http://127.0.0.1:3000
# Windows PowerShell
Get-NetTCPConnection -LocalPort 3000 -State Listen -ErrorAction SilentlyContinue
curl.exe -I http://127.0.0.1:3000
- لا يظهر مستمع، و
curlيقولConnection refused: الخادم متوقف، أو انهار عند الإقلاع، أو يعمل على منفذ آخر. ارجع للطرفية وشغّله أو اقرأ أول خطأ فيها. - يظهر مستمع و
curlيعيد استجابة HTTP: الخادم سليم، والفرق في الرابط الذي تفتحه في المتصفح. قارنه حرفًا حرفًا: المنفذ، والعنوان (localhostأم127.0.0.1أم IP الشبكة)، والمسار. - يعمل داخل Docker فقط: وجود المستمع داخل الحاوية لا يكفي. انشر المنفذ
للمضيف، وتأكد أن التطبيق يستمع على
0.0.0.0داخلها.
افهم الرسالة: رفض، لا مهلة
عندما تفتح localhost:3000 يحاول المتصفح فتح اتصال TCP مع منفذ 3000 في جهازك
نفسه. الرفض الفوري يعني أن نظام التشغيل ردّ: «لا يوجد أي برنامج يستمع
هنا». لا علاقة للإنترنت ولا للواي فاي: الرحلة كلها داخل جهازك.
قراءة اسم الخطأ بدقّة توفّر عليك نصف التشخيص، لأن كروم يعطي لكل فشل اسمًا مختلفًا:
| الخطأ | ماذا حدث فعلًا | أين تبحث |
|---|---|---|
ERR_CONNECTION_REFUSED | وصلنا للجهاز، ولا برنامج على المنفذ | تشغيل الخادم، رقم المنفذ، عنوان الاستماع |
ERR_CONNECTION_TIMED_OUT | لا ردّ إطلاقًا، الطلب ضاع في الطريق | جدار حماية يُسقط الحزم بصمت، أو عنوان IP خاطئ |
ERR_NAME_NOT_RESOLVED | فشل تحويل اسم النطاق إلى IP | DNS أو خطأ إملائي في اسم الموقع |
ERR_SSL_PROTOCOL_ERROR | يوجد خادم، لكنك فتحت https:// وهو يتكلم http | احذف الـ s من الرابط |
404 أو 500 | خادم موجود استقبل طلبك وردّ | الكود والمسارات: 404 و500 |
جرّبنا ذلك فعليًا في Chromium: خادم Node يستمع على http://127.0.0.1:3999.
فتحُ منفذ مغلق بجانبه أعطى ERR_CONNECTION_REFUSED، وفتحُ نفس الخادم بـ
https:// أعطى ERR_SSL_PROTOCOL_ERROR. خطآن مختلفان، ولكلٍّ منهما حلّ مختلف.
الأسباب بالترتيب الواقعي
| السبب | كيف تتأكد | الحل |
|---|---|---|
| الخادم غير شغّال أصلًا | لا سطر Ready بالطرفية | شغّله 😄 |
| انهار لحظة الإقلاع | رسالة خطأ حمراء بالطرفية | اقرأها وعالجها |
| منفذ مختلف | «3001» بالطرفية و«3000» بالمتصفح | طابق الرقم |
| داخل Docker بلا نشر | يعمل داخل الحاوية فقط | -p 3000:3000 |
| عنوان استماع مختلف | يعمل بـ 127.0.0.1 ويرفض بـ localhost أو العكس | وحّد العنوان أو استمع على الكل |
1) الخادم ببساطة غير شغّال
الطرفية أُغلقت، أو أوقفته بـ Ctrl+C ونسيت، أو فتحت نافذة جديدة وظننت القديمة تعمل. العلامة القاطعة: طرفية الخادم يجب أن تعرض سطرًا حيًّا مثل:
- Local: http://localhost:3000
✓ Ready in 1.2s
لا سطر؟ لا خادم. شغّله وأبقِ الطرفية مفتوحة.
2) انهار أثناء الإقلاع، والجواب فوق مباشرة
شغّلته فعلًا، لكنه مات خلال ثانية برسالة حمراء لم تقرأها. أشهر رسالتين:
EADDRINUSE: المنفذ محجوز من تشغيلة قديمة. دليلها الكامل هنا.Cannot find module ...: اعتماديات ناقصة. نفّذnpm installثم أعد التشغيل (التفاصيل).
القاعدة: اقرأ آخر 10 أسطر في الطرفية قبل فتح المتصفح.
3) المنفذ غير الذي تظن
أدوات كثيرة تنتقل لمنفذ آخر تلقائيًا عندما يكون الافتراضي محجوزًا. Next.js 16 مثلًا يكتب:
⚠ Port 3000 is in use by process 4821, using available port 3001 instead.
وأنت ما زلت تفتح 3000، فيجيبك خادم قديم منسي أو لا يجيبك أحد. الحل: انسخ الرابط من الطرفية نفسها، ولا تكتبه من الذاكرة. والأفضل أن توقف العملية القديمة (رقمها مذكور في الرسالة) بدل العمل على منفذين.
4) يعمل داخل Docker، والباب مغلق للمضيف
الخادم يستمع داخل الحاوية، وجهازك عالم آخر له منافذه الخاصة. بلا نشر المنفذ لن يصلك شيء:
docker run -p 3000:3000 my-app # منفذ المضيف:منفذ الحاوية
docker ps # تأكد أن عمود PORTS يعرض 0.0.0.0:3000->3000/tcp
وداخل الحاوية يجب أن يستمع تطبيقك على 0.0.0.0 لا على 127.0.0.1. فالـ
127.0.0.1 داخل الحاوية يعني الحاوية نفسها فقط، فيرفض كل ما يأتي من خارجها
حتى مع النشر. شرح كامل في درس نشر المنافذ،
وتعارضات المنفذ نفسها في دليل Docker هنا.
5) localhost مقابل 127.0.0.1 مقابل ::1
localhost اسم، لا عنوان. النظام يحوّله إلى 127.0.0.1 (IPv4) أو ::1
(IPv6)، والخادم قد يستمع على أحدهما فقط. والنتيجة محيّرة: الخادم شغّال،
والرابط بالاسم يُرفض.
جرّبنا خادم Node يستمع على 127.0.0.1 فقط: الطلب إلى http://127.0.0.1:3999
نجح، والطلب إلى http://[::1]:3999 فشل، لأنه لا أحد يستمع على عنوان IPv6.
ومنذ Node.js 17 لم يعد Node يقدّم عناوين IPv4 تلقائيًا عند تحويل الأسماء، بل
يأخذها بالترتيب الذي يعطيه النظام، فقد يصبح localhost هو ::1 على جهاز
ويبقى 127.0.0.1 على جهاز آخر. لهذا تظهر المشكلة عند زميلك ولا تظهر عندك.
الحل العملي:
- جرّب
http://127.0.0.1:3000ثمhttp://[::1]:3000. أيهما يعمل يخبرك أين يستمع الخادم. - ثبّت العنوان في الطرفين: اجعل الخادم يستمع على
127.0.0.1واكتب نفس العنوان في المتصفح وفي إعدادات الـ proxy، أو اجعله يستمع على كل الواجهات (0.0.0.0، أو--hostفي Vite) إن كنت تحتاج الوصول من أجهزة أخرى.
الخطأ في Console: الواجهة لا تصل للـ API
نسخة مختلفة من نفس الخطأ: الصفحة نفسها تفتح عادي، لكن البيانات لا تظهر، وفي أدوات المطوّر (F12) تجد:
Failed to load resource: net::ERR_CONNECTION_REFUSED
TypeError: Failed to fetch
هذا ما ظهر بالضبط عندما جرّبنا fetch("http://127.0.0.1:5000/api") من صفحة
تعمل، وخادم المنفذ 5000 متوقف. المعنى: الواجهة الأمامية شغّالة، والخلفية
لا. افحص بالترتيب:
- هل الـ backend يعمل فعلًا؟ افتح رابط الـ API نفسه في تبويب جديد، أو
نفّذ
curlعليه. - هل رابطه صحيح؟ غالبًا يأتي من متغير بيئة (
VITE_API_URLأوNEXT_PUBLIC_API_URL) يشير لمنفذ قديم. وتذكّر أن تغيير ملف.envيحتاج إعادة تشغيل خادم التطوير غالبًا. - هل هي مشكلة CORS؟ لا. في CORS يصل الطلب للخادم ويردّ، والمتصفح يرفض تسليم الردّ لصفحتك، ورسالته تذكر CORS صراحة. الفرق وحلّه هنا.
فخ Docker Compose: إن كان الطلب يخرج من حاوية إلى حاوية أخرى (خادم
Next.js يطلب الـ API مثلًا)، فـ localhost داخلها يعني الحاوية نفسها. استخدم
اسم الخدمة: http://api:5000. أما الطلب الخارج من المتصفح فيستخدم المنفذ
المنشور على جهازك: http://localhost:5000.
(الشبكات في Docker)
من الجوال أو المحاكي: localhost ليس حاسوبك
تختبر تطبيق Flutter أو React Native وتطلب http://localhost:3000، فيأتيك الرفض
رغم أن الخادم يعمل على حاسوبك؟ هذا متوقع: localhost في الجوال أو المحاكي
يعني الجوال نفسه.
- محاكي أندرويد: العنوان
10.0.2.2هو اسم مستعار خاص يشير إلى127.0.0.1في حاسوبك:http://10.0.2.2:3000. - جوال حقيقي على نفس الواي فاي: استخدم IP حاسوبك في الشبكة (من
ipconfigعلى Windows أوip addrعلى Linux)، مثلhttp://192.168.1.20:3000. ويجب أن يستمع الخادم على0.0.0.0لا على127.0.0.1، وقد يسألك جدار حماية Windows أول مرة: اسمح له على الشبكة الخاصة.
على موقع حقيقي لا على localhost
إن ظهر ERR_CONNECTION_REFUSED على موقع إنترنت عادي فأنت لست أمام مشكلة تطوير.
السؤال الأول: هل المشكلة عندك أم عند الموقع؟
- افتح الموقع من شبكة أخرى، ببيانات الجوال مثلًا. إن عمل هناك، فالسبب في جهازك أو شبكتك. وإن فشل من كل مكان، فخادم الموقع متوقف، ولا حلّ لديك إلا الانتظار.
- إن كان السبب عندك، أوقف الـ VPN مؤقتًا، وجرّب تعطيل جدار الحماية أو برنامج الحماية دقيقة واحدة.
- افحص ملف
hosts(C:\Windows\System32\drivers\etc\hostsعلى Windows، و/etc/hostsعلى macOS/Linux): سطر قديم مثل127.0.0.1 example.comمن مشروع سابق يوجّه الموقع إلى جهازك، فيرفضه جهازك لأنه لا خادم عليه.
وإن كنت صاحب الخادم (VPS مثلًا): الرفض يعني أن الطلب وصل للخادم ولا برنامج
على المنفذ. فالمشكلة غالبًا ليست في جدار الحماية، لأن جدار الحماية الذي يُسقط
الحزم بصمت يعطي TIMED_OUT لا REFUSED. افحص الخدمة نفسها:
sudo ss -ltnp | grep -E ':80|:443' # من يستمع على 80 و443؟
sudo systemctl status nginx # هل Nginx يعمل؟ ولماذا توقّف؟
ولاحظ في ناتج ss: سطر بالعنوان 127.0.0.1:3000 يعني أن التطبيق يقبل
الاتصالات من الخادم نفسه فقط (خلف Nginx مثلًا)، وسطر بالعنوان 0.0.0.0:3000
أو *:3000 يعني أنه مفتوح لكل الواجهات.
قائمة تحقّق سريعة
- الطرفية تعرض سطر Ready/Listening الآن؟
lsofأوGet-NetTCPConnectionيُظهر مستمعًا على المنفذ؟- إن مات عند الإقلاع: اقرأ الخطأ (EADDRINUSE أو module ناقص غالبًا).
- انسخ الرابط والمنفذ من الطرفية حرفيًا، و
httpلاhttps. - Docker؟ →
-pللنشر + استماع على0.0.0.0داخل الحاوية. - جرّب
127.0.0.1و[::1]بدلlocalhost. - جوال أو محاكي؟ →
10.0.2.2أو IP الحاسوب، لاlocalhost. - موقع حقيقي؟ → جرّب شبكة أخرى، ثم VPN وملف
hosts.
الخلاصة
refused to connect لا يكذب: لا برنامج يستمع حيث تطرق. الحل دائمًا في جهة
التشغيل: شغّل الخادم، واقرأ الطرفية، وطابق المنفذ والعنوان، وانشر منفذ Docker.
ولن تراه مجددًا إلا عن نسيان مشروع 😄
لتفهم المنافذ والعناوين من الجذور، اقرأ المنافذ والـ Sockets وتشخيص مشاكل الشبكات في مسار الشبكات بالعربي، وأكمل السلسلة مع حل EADDRINUSE.