“This site can't be reached.” “localhost refused to connect.” A blank white page. Every one of these is a clue. Match your symptom, see what's actually happening inside your computer, and fix it — with exact commands for macOS, Windows and Linux.
Your browser knocks on one numbered door — a port. If no program is listening behind it, your operating system turns the connection away instantly. That instant “no” is ERR_CONNECTION_REFUSED.
It's actually good news: your computer is fine, and the network is fine. The only question is why nothing is listening — usually a stopped server or the wrong port.
Try it: your app starts switched off. Knock on :3000, flip the switch, and knock again.
Fig. 02 — Knock on a doorInteractive
·
Your app is switched off
Send a request to :3000 and watch what your computer does.
§ 03 The fixes
Every common cause, and its fix
01
“localhost refused to connect”
ERR_CONNECTION_REFUSED · ECONNREFUSED · Firefox: “Unable to connect”
The request reached your computer, but no program is listening on that port. Work through these in order:
Is the server still running? Go back to the terminal where you started it. If it crashed, the error is near the bottom; if you closed the window or your laptop slept, it's simply gone. Start it again — npm run dev, python manage.py runserver, docker start my-app — and wait until it prints an address.
Are you on the right port? Use the exact URL the server printed. Many tools quietly move to the next free port (“Port 3000 is in use, trying 3001 instead”).
Is anything listening at all? Ask your OS directly. No output means nothing is behind that door.
Try 127.0.0.1 instead of localhost. If that works, jump to the IPv6 mismatch.
macOS
Terminal
# Is anything listening on port 3000?
lsof -nP -iTCP:3000 -sTCP:LISTEN
# Talk to it directly — "Connection refused" confirms it
curl -v http://localhost:3000
Linux
Terminal
# Is anything listening on port 3000?
ss -ltn 'sport = :3000'
# Talk to it directly — "Connection refused" confirms it
curl -v http://localhost:3000
Windows
PowerShell
# Is anything listening on port 3000?
Get-NetTCPConnection -LocalPort 3000 -State Listen
# Talk to it directly
curl.exe -v http://localhost:3000
02
“Port already in use”
Error: listen EADDRINUSE: address already in use :::3000 · OSError: [Errno 48] Address already in use
Only one program can listen on a port. Another one — very often an old copy of your own server running in a forgotten terminal — already has it. Find it and stop it, or start your app on a different port.
macOS
Terminal
# Find the process holding port 3000
lsof -nP -iTCP:3000 -sTCP:LISTEN
# Stop it (PID is the second column)
kill <PID>
# Still there? Force it
kill -9 <PID>
Linux
Terminal
# Find the process holding port 3000
sudo ss -ltnp 'sport = :3000'
# Stop it — or kill whatever holds the port in one step
kill <PID>
sudo fuser -k 3000/tcp
Windows
PowerShell
# Find the process holding port 3000 (OwningProcess is its PID)
Get-NetTCPConnection -LocalPort 3000 -State Listen
# Stop it
Stop-Process -Id <PID> -Force
In Command Prompt: netstat -ano | findstr :3000, then taskkill /PID <PID> /F.
Or leave it running and pick another port: npm run dev -- --port 5174 (Vite), next dev -p 3001, flask run --port 5001, python manage.py runserver 8001.
Mac, port 5000 or 7000?Since macOS Monterey, AirPlay Receiver listens there. Turn it off in System Settings → General → AirDrop & Handoff, or use another port.
03
127.0.0.1 works, but localhost doesn't
connect ECONNREFUSED ::1:3000 · works in one tool, refused in another
localhost can resolve to two addresses: 127.0.0.1 (IPv4) and ::1 (IPv6). If your server listens on only one of them and your browser or tool tries the other, the connection is refused — even though the server is running. Node.js 17 and later stopped preferring IPv4 for localhost, which made this a common surprise.
Fix: point your client at the address that works (http://127.0.0.1:3000), or make the server listen on both — for example server.listen(3000, "::") in Node.js accepts IPv6 and, on most systems, IPv4 too.
04
Works on my laptop, but not on my phone
Phone shows “can't connect” · another computer on the same Wi-Fi can't reach it
On your phone, localhost means the phone. To reach your laptop you need three things:
Your laptop's LAN address, like 192.168.1.23 — not localhost.
A server listening on 0.0.0.0, not only 127.0.0.1 (Vite: npm run dev -- --host; Flask: --host=0.0.0.0; Django: runserver 0.0.0.0:8000).
The same network, and a firewall that allows it. Guest and office Wi-Fi often isolate devices from each other.
Docker: the container is up, but localhost won't load
docker ps shows “Up” · browser shows refused or an empty response
Two things must both be true. The port must be published from the container to your computer with -p HOST:CONTAINER, and the app inside must listen on 0.0.0.0 — because inside a container, 127.0.0.1 means the container itself.
Fig. 03 — docker run -p 8080:80Live
Terminal
# Wrong: the container runs, but no port is published
docker run my-app
# Right: your localhost:8080 → the container's port 80
docker run -p 8080:80 my-app
# Check the PORTS column for 0.0.0.0:8080->80/tcp
docker ps
With Docker Compose, the same mapping goes under ports: ["8080:80"]. Going the other way — reaching a server on your computer from inside a container — use host.docker.internal instead of localhost (Docker Desktop).
06
WSL: works in Linux, not in my Windows browser
curl works inside WSL · Edge or Chrome on Windows can't connect
WSL 2 runs Linux in a lightweight virtual machine with its own network. Windows normally forwards localhost into it automatically; when that fails, make the server listen on 0.0.0.0, try the WSL address from hostname -I, or switch WSL to mirrored networking on Windows 11, which shares your Windows network with Linux:
%UserProfile%\.wslconfig
[wsl2]
networkingMode=mirrored
Then run wsl --shutdown and start your distribution again.
Most dev servers speak plain http://. If your browser autocompletes or upgrades to https://, there's nothing on the other end that speaks encryption. Type http://localhost:3000 explicitly.
If Chrome keeps forcing HTTPS for localhost (an old HSTS rule), open chrome://net-internals/#hsts and delete the domain security policy for localhost. And if you genuinely need HTTPS locally, create a certificate your machine trusts:
Terminal · mkcert
mkcert -install
mkcert localhost 127.0.0.1 ::1
08
“This site can't be reached — took too long to respond”
ERR_CONNECTION_TIMED_OUT · the tab spins forever
A refusal is instant; a timeout means packets are being dropped or the server is stuck. Usual suspects: a firewall, VPN, proxy or antivirus intercepting local traffic; a server frozen in a busy loop; or an IP address that isn't yours anymore because you changed Wi-Fi.
Terminal · give up after 5 seconds
curl -v --max-time 5 http://localhost:3000
Pause the VPN or security tool and try again. If that fixes it, allow your port in its settings. If the server itself hangs, restart it and watch its logs.
09
A blank page, or an old version of my app
White screen · changes don't show up · works in a private window
A blank page is often a crashed frontend: open the browser console (F12) and look for red errors. Old versions usually come from caches and service workers, which can keep serving a previous build even after your server changed.
Hard refresh: ⌘ShiftR on macOS, CtrlShiftR on Windows and Linux.
DevTools → Application → Service workers → Unregister, then reload.
Rule it out completely with a private window or another browser.
10
localhost goes somewhere strange
localhost resolves to the wrong address · works on every machine but yours
Your hosts file maps names to addresses before any DNS server is asked, and a tool or tutorial may have edited it. It should contain 127.0.0.1 localhost and usually ::1 localhost.
macOS / Linux · Windows
cat /etc/hosts
# Windows:
type C:\Windows\System32\drivers\etc\hosts
Fix any other line that mentions localhost (you'll need admin rights to save the file).
11
My friend can't open my localhost link
http://localhost:3000 works for you · “refused to connect” for everyone else
That's not a bug — it's what localhost means. On your friend's device, localhost is their computer, where nothing is running. There are two real fixes:
Quick demo: a tunnel such as cloudflared tunnel --url http://localhost:3000 or ngrok http 3000 gives your laptop a temporary public URL. It stops working when your laptop sleeps — and anyone with the link can reach your app.
The durable fix: deploy it to a server in the cloud that stays on and has a public address. Here's how, in one sentence.
A deployed app has a real address that works on every phone, every network and every friend's laptop — even with yours closed. Learn what the cloud actually is, or skip ahead and deploy in one sentence.
§ 05 FAQ
Quick answers
What does “localhost refused to connect” mean?
Your computer received the request, but no program was listening on that port, so the operating system refused the connection immediately. The server you meant to reach isn't running, crashed, or is on a different port than the one in the URL.
How do I fix ERR_CONNECTION_REFUSED on localhost?
Start (or restart) your server, then open the exact address and port it prints. If it still fails, check whether anything is listening on that port, and try 127.0.0.1 instead of localhost in case of an IPv4/IPv6 mismatch.
How do I free a port that's already in use?
Find the process that owns the port — lsof -nP -iTCP:3000 -sTCP:LISTEN on macOS, ss -ltnp on Linux, netstat -ano | findstr :3000 on Windows — and stop it with kill <PID> or taskkill /PID <PID> /F. Or start your app on another port.
Why does 127.0.0.1 work but localhost doesn't?
localhost can resolve to the IPv6 address ::1 as well as 127.0.0.1. If your server only listens on IPv4 and your browser or tool tries IPv6 first, the connection is refused. Use 127.0.0.1 explicitly, or make the server listen on both.
Why can't I open my localhost site on my phone?
On your phone, localhost means the phone. Use your computer's LAN address (like 192.168.1.23:3000), make the dev server listen on 0.0.0.0, and keep both devices on the same network.
Why can't I reach my Docker container on localhost?
Publish the port with docker run -p 8080:80 and make sure the app inside the container listens on 0.0.0.0, not 127.0.0.1 — inside a container, 127.0.0.1 is the container itself.