CH. 03 Troubleshooting · diagnose, then fix

Localhost not working?

“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.

Fig. 01 — The most common error on the smallest networkRedlined
The “localhost refused to connect” error page, annotated A browser showing “This site can't be reached — localhost refused to connect — ERR_CONNECTION_REFUSED”, with three red annotations: check the port in the address bar, nothing is listening on that port, and the instant refusal comes from your own computer. localhost:3000 This site can't be reached localhost refused to connect. Try: checking the connection Checking the proxy and the firewall ERR_CONNECTION_REFUSED Reload 1 WRONG PORT?CHECK WHAT YOUR SERVERPRINTED WHEN IT STARTED 2 NOTHING LISTENINGSERVER STOPPED, CRASHEDOR ON ANOTHER PORT 3 INSTANT REFUSALYOUR COMPUTER ANSWERED —YOUR SERVER DIDN'T

§ 02 What's really happening

“Refused” means the door was shut

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
A computer drawn as a building with numbered doors The computer at 127.0.0.1 is a building. Each door is a port: 22, 80, 443, 3000, 3001, 5173, 5432, 6000, 8080 and any other number. Doors with a green light have a program listening behind them. The browser sends a request to one door at a time. localhost:3000 YOUR BROWSERON THIS COMPUTER 127.0.0.1 · LOCALHOST 65,535 DOORS PER ADDRESS · PORT 0 IS RESERVED 22SSH 80HTTP 443HTTPS 3000YOUR APP 3001EMPTY 5173VITE 5432POSTGRES 6000X11 8080ALT HTTP …ANY PORT

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:

  1. 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.
  2. 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”).
  3. Is anything listening at all? Ask your OS directly. No output means nothing is behind that door.
  4. 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.

Terminal · see which family answers
curl -4 http://localhost:3000   # IPv4: 127.0.0.1
curl -6 http://localhost:3000   # IPv6: ::1

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:

  1. Your laptop's LAN address, like 192.168.1.23 — not localhost.
  2. 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).
  3. The same network, and a firewall that allows it. Guest and office Wi-Fi often isolate devices from each other.

macOS

Terminal · your LAN address
ipconfig getifaddr en0

Linux

Terminal · your LAN address
hostname -I

Windows

Command Prompt · look for “IPv4 Address”
ipconfig

Then open http://192.168.1.23:3000 (with your address and port) on the phone. See who can reach what, interactively →

05

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
How Docker port publishing works The browser on your computer opens localhost:8080. Docker forwards port 8080 on your computer to port 80 inside the container, where the app must be listening on 0.0.0.0. If the app listens on 127.0.0.1, only the container itself can reach it. YOUR COMPUTER · THE HOST localhost:8080 BROWSER CONTAINER · my-app APP listening on 0.0.0.0:80 :8080 :80 -p 8080:80 ON 127.0.0.1, ONLY THE CONTAINER CAN REACH IT
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.

07

“Your connection is not private” on localhost

https://localhost:3000 fails · NET::ERR_CERT_AUTHORITY_INVALID · ERR_SSL_PROTOCOL_ERROR

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: ⌘ Shift R on macOS, Ctrl Shift R 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.

§ 04 Make it permanent

Fixed it? Now stop depending on localhost.

Deploy my app

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.