تخطَّ إلى المحتوى

١٢ يوليو ٢٠٢٦ · آخر تحديث: ١٩ أكتوبر ٢٠٢٦

حل خطأ localhost refused to connect بالعربي

شارك المقال:

كتبت 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فشل تحويل اسم النطاق إلى IPDNS أو خطأ إملائي في اسم الموقع
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) انهار أثناء الإقلاع، والجواب فوق مباشرة

شغّلته فعلًا، لكنه مات خلال ثانية برسالة حمراء لم تقرأها. أشهر رسالتين:

القاعدة: اقرأ آخر 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 متوقف. المعنى: الواجهة الأمامية شغّالة، والخلفية لا. افحص بالترتيب:

  1. هل الـ backend يعمل فعلًا؟ افتح رابط الـ API نفسه في تبويب جديد، أو نفّذ curl عليه.
  2. هل رابطه صحيح؟ غالبًا يأتي من متغير بيئة (VITE_API_URL أو NEXT_PUBLIC_API_URL) يشير لمنفذ قديم. وتذكّر أن تغيير ملف .env يحتاج إعادة تشغيل خادم التطوير غالبًا.
  3. هل هي مشكلة 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.

📚 مصادر رسمية للتعمّق: توثيق HTTP والشبكات على MDN

الأسئلة الشائعة

ما الفرق بين هذا الخطأ وخطأ 404 أو 500؟

404 و500 تعني أن خادمًا موجودًا واستقبل طلبك ثم ردّ (بصفحة غير موجودة أو بعطل داخلي). أما refused to connect فتعني أنه لا يوجد برنامج يستمع على هذا العنوان والمنفذ، فلم يصل الطلب لأي تطبيق. لذلك حلّه دائمًا في جهة تشغيل الخادم لا في كود الصفحة.

الطرفية تقول الخادم شغال والمتصفح يرفض — كيف؟

أشهر ثلاثة أسباب: المنفذ في الطرفية غير الذي كتبته في المتصفح (يعمل على 3001 وأنت تفتح 3000)، أو الخادم داخل Docker/WSL بلا نشر منفذ للمضيف، أو الخادم يستمع على عنوان واحد (127.0.0.1 مثلًا) وأنت تطرق عنوانًا آخر (::1 أو IP الشبكة). طابق الرقم والعنوان حرفيًا مع سطر Listening.

ظهر net::ERR_CONNECTION_REFUSED في Console مع Failed to fetch، ما معناه؟

الصفحة نفسها تعمل، لكن طلب fetch منها إلى الـAPI لم يجد خادمًا. غالبًا الواجهة الخلفية متوقفة، أو رابطها في متغير البيئة يشير لمنفذ خاطئ. هذا ليس خطأ CORS: في CORS يصل الطلب للخادم ويردّ، ورسالة المتصفح تذكر CORS صراحة.

يظهر ERR_CONNECTION_REFUSED على موقع إنترنت عادي، لا على localhost؟

افتح الموقع من شبكة أخرى (بيانات الجوال مثلًا). إن عمل هناك، فالسبب في جهازك أو شبكتك: VPN، أو برنامج حماية، أو سطر في ملف hosts يوجّه الموقع إلى 127.0.0.1. وإن فشل من كل مكان، فخادم الموقع نفسه متوقف ولا حلّ لديك إلا الانتظار أو التواصل مع أصحابه.

اقرأ أيضًا

تصفّح كل المقالات في المدوّنة، أو ابدأ التعلّم من المسارات و خرائط الطريق.