Fix: ERR_CONNECTION_REFUSED (localhost refused to connect)
The Error
You open http://localhost:3000 in your browser and get one of these:
Chrome:
This site can't be reached
localhost refused to connect.
ERR_CONNECTION_REFUSEDFirefox:
Unable to connect
Firefox can't establish a connection to the server at localhost:3000.Edge:
Hmmm… can't reach this page
localhost refused to connect.All of these mean the same thing: your browser tried to connect to localhost on a specific port, and nothing was listening there.
Why This Happens
The browser sends a TCP connection request to 127.0.0.1 (or ::1 for IPv6) on the port you specified. If no process is accepting connections on that address and port, the operating system immediately rejects the connection with a TCP RST packet. The browser translates that rejection into ERR_CONNECTION_REFUSED.
Common causes:
- Your dev server isn’t running. You forgot to start it, it crashed, or it’s still booting up.
- Wrong port. The server is running on port 5173 but you’re hitting port 3000.
- The server is bound to the wrong address. It’s listening on
127.0.0.1but you’re accessing0.0.0.0, or vice versa. - Docker port not mapped. The server runs inside a container but the port isn’t published to the host.
- WSL2 localhost forwarding isn’t working. The server runs in WSL2 but the Windows browser can’t reach it.
- A firewall or antivirus is blocking the port.
- HTTPS vs HTTP mismatch. The server serves HTTP but you’re requesting HTTPS (or the reverse).
- Browser cache or HSTS is forcing HTTPS on a localhost URL.
Fix 1: Start Your Dev Server
This is the most common cause. Make sure your dev server is actually running.
# Check if anything is listening on the port
# macOS / Linux
lsof -i :3000
# Windows (cmd)
netstat -ano | findstr :3000If nothing shows up, start your server:
# React (create-react-app)
npm start
# Next.js
npm run dev
# Vite
npm run dev
# Express
node server.jsWatch the terminal output. The server should print which port it’s listening on. If it crashes immediately, the error in the terminal is your real problem — fix that first.
Fix 2: Check the Port
Your server might be running on a different port than what you’re typing in the browser. Dev servers print the URL when they start:
➜ Local: http://localhost:5173/If Vite says 5173 and you’re going to localhost:3000, that’s your problem. Use the URL the server gives you.
Common default ports:
| Framework | Default Port |
|---|---|
| Vite | 5173 |
| Create React App | 3000 |
| Next.js | 3000 |
| Angular CLI | 4200 |
| Vue CLI | 8080 |
| Express | 3000 (by convention) |
| Django | 8000 |
| Flask | 5000 |
| Rails | 3000 |
If the default port was already in use, many dev servers auto-increment to the next available port (e.g., 3001, 5174). Check the terminal output carefully.
Related: If the port is taken by another process, see Fix: Port 3000 Is Already in Use.
Fix 3: Fix the Server Bind Address (0.0.0.0 vs 127.0.0.1)
Some dev servers bind to 127.0.0.1 (loopback only) by default. This is usually fine, but it can cause problems in specific setups — particularly Docker containers and VMs.
If your server binds to 127.0.0.1 inside a Docker container, it only accepts connections from inside the container. You need it to bind to 0.0.0.0 to accept connections from outside.
Vite:
// vite.config.js
export default defineConfig({
server: {
host: '0.0.0.0'
}
});Or from the command line:
npx vite --host 0.0.0.0Next.js:
npx next dev -H 0.0.0.0webpack-dev-server:
// webpack.config.js
module.exports = {
devServer: {
host: '0.0.0.0'
}
};Express:
// Explicitly bind to all interfaces
app.listen(3000, '0.0.0.0', () => {
console.log('Server running on port 3000');
});Django:
python manage.py runserver 0.0.0.0:8000Flask:
flask run --host=0.0.0.00.0.0.0 means “listen on all network interfaces.” 127.0.0.1 means “only accept connections from this machine.” For local development on your own machine, either works. Inside containers or VMs, you need 0.0.0.0.
Fix 4: Docker Port Mapping
If your server runs inside a Docker container, the port must be mapped to your host machine with the -p flag.
Wrong — no port mapping:
docker run myappRight — map container port 3000 to host port 3000:
docker run -p 3000:3000 myappIn docker-compose.yml:
services:
app:
build: .
ports:
- "3000:3000"Verify the port mapping is active:
docker ps --format "table {{.Names}}\t{{.Ports}}"You should see something like:
NAMES PORTS
myapp 0.0.0.0:3000->3000/tcpIf you see the port listed but still get ERR_CONNECTION_REFUSED, the server inside the container is probably bound to 127.0.0.1 instead of 0.0.0.0. See Fix 3.
Related: If Docker gives you permission errors, see Fix: docker: permission denied while trying to connect to the Docker daemon socket.
Fix 5: WSL2 Localhost Forwarding
When running a dev server inside WSL2, Windows should automatically forward localhost requests to the WSL2 VM. But this doesn’t always work.
Check if the server is reachable from inside WSL2 first:
curl http://localhost:3000If curl works inside WSL2 but the Windows browser can’t connect:
Option A: Use the WSL2 IP address directly.
Find your WSL2 IP:
hostname -I | awk '{print $1}'Use that IP in your Windows browser (e.g., http://172.25.160.1:3000).
Option B: Bind to 0.0.0.0.
Make sure your dev server binds to 0.0.0.0, not 127.0.0.1 (see Fix 3). WSL2 localhost forwarding requires the server to listen on all interfaces.
Option C: Restart WSL2 localhost forwarding.
In PowerShell (as Administrator):
wsl --shutdownThen reopen your WSL2 terminal and start the server again. This resets the networking layer between Windows and WSL2.
Option D: Add a port proxy rule manually.
If automatic forwarding keeps failing, set up a manual port proxy in PowerShell (as Administrator):
netsh interface portproxy add v4tov4 listenport=3000 listenaddress=127.0.0.1 connectport=3000 connectaddress=$(wsl hostname -I | ForEach-Object { $_.Trim() })Remove it later with:
netsh interface portproxy delete v4tov4 listenport=3000 listenaddress=127.0.0.1Fix 6: HTTPS vs HTTP Mismatch
If your server runs on HTTP but you’re requesting https://localhost:3000, the connection is refused because nothing is listening for TLS connections on that port.
Check your browser address bar. Some browsers hide the protocol, so you might not notice you’re on HTTPS. Click the address bar to see the full URL.
Common causes of accidental HTTPS:
- HSTS (HTTP Strict Transport Security). If you previously ran a local HTTPS server and it sent an HSTS header, the browser remembers and forces HTTPS on all future requests to that host+port.
To clear HSTS for localhost in Chrome:
- Go to
chrome://net-internals/#hsts - Under “Delete domain security policies,” enter
localhost - Click “Delete”
Browser auto-upgrading HTTP to HTTPS. Some browsers (Chrome 115+) may try HTTPS first for certain domains. For localhost, this usually isn’t the case, but check the URL.
A proxy or extension is forcing HTTPS. HTTPS Everywhere or similar extensions can redirect localhost to HTTPS. Disable them for localhost.
Fix 7: Clear Browser DNS Cache
Your browser caches DNS lookups. If localhost is resolving to the wrong address, clear it.
Chrome:
- Go to
chrome://net-internals/#dns - Click “Clear host cache”
Also flush your OS DNS cache:
# macOS
sudo dscacheutil -flushcache && sudo killall -HUP mDNSResponder
# Linux (systemd-resolved)
sudo systemd-resolve --flush-caches
# Windows (cmd, as admin)
ipconfig /flushdnsFix 8: Check Your hosts File
Your hosts file might have an entry overriding where localhost resolves to.
macOS / Linux:
cat /etc/hostsWindows:
type C:\Windows\System32\drivers\etc\hostsYou should see a line like:
127.0.0.1 localhostIf localhost is pointed at a different IP, or if the line is missing entirely, fix it. On some systems, if the localhost entry is missing, the OS falls back to DNS resolution, which can return unexpected results.
Also check for IPv6. If your server only listens on IPv4 (127.0.0.1) but localhost resolves to the IPv6 loopback (::1), the connection is refused. Make sure your hosts file includes both:
127.0.0.1 localhost
::1 localhostOr configure your server to listen on both IPv4 and IPv6.
Fix 9: Firewall or Antivirus Blocking the Port
Firewalls and security software can block local ports.
Windows Firewall:
- Open “Windows Defender Firewall with Advanced Security”
- Check Inbound Rules for any rules blocking the port
- Add an exception if needed, or temporarily disable the firewall to test:
# Test with firewall off (PowerShell as Admin)
Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled False
# Re-enable after testing
Set-NetFirewallProfile -Profile Domain,Public,Private -Enabled TruemacOS:
# Check if the firewall is on
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --getglobalstate
# Temporarily disable
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate off
# Re-enable after testing
sudo /usr/libexec/ApplicationFirewall/socketfilterfw --setglobalstate onLinux (iptables/nftables):
# Check for rules blocking the port
sudo iptables -L -n | grep 3000Corporate VPNs and antivirus software (like Norton, Kaspersky, or Bitdefender) can also block local ports. If the error started after installing or updating such software, try temporarily disabling it to confirm.
Fix 10: Check Proxy Settings
If your system or browser is configured to use a proxy, requests to localhost might be routed through the proxy instead of going directly to your machine.
Chrome: Go to chrome://settings/ → search “proxy” → Open your system proxy settings. Make sure localhost and 127.0.0.1 are in the proxy bypass list.
Environment variables: Check if HTTP_PROXY or HTTPS_PROXY is set:
echo $HTTP_PROXY
echo $HTTPS_PROXY
echo $NO_PROXYAdd localhost to NO_PROXY:
export NO_PROXY=localhost,127.0.0.1npm proxy config can also interfere with dev servers:
npm config get proxy
npm config get https-proxyIf these are set and you don’t need them:
npm config delete proxy
npm config delete https-proxyStill Not Working?
The Server Starts But Crashes Immediately
Watch the terminal output carefully. The server might start, print its port, then crash a millisecond later. Common causes:
- Missing environment variables (database URL, API keys)
- A syntax error in a config file
- A missing dependency (
npm installnot run after pulling changes) - The database or a dependent service isn’t running
Fix the crash first. The ERR_CONNECTION_REFUSED is just a symptom.
IPv4 vs IPv6 Mismatch
Some systems resolve localhost to ::1 (IPv6) but the server only listens on 127.0.0.1 (IPv4). Node.js servers are particularly prone to this.
Test both directly in your browser:
http://127.0.0.1:3000(IPv4)http://[::1]:3000(IPv6)
If one works and the other doesn’t, the server is only listening on one protocol. Fix the server to listen on both, or use the working address.
In Node.js, app.listen(3000) without specifying a host listens on both IPv4 and IPv6 in most versions. But app.listen(3000, 'localhost') may only resolve to one. Use app.listen(3000, '0.0.0.0') for explicit IPv4 or app.listen(3000) for dual-stack.
nginx Reverse Proxy Returns Connection Refused
If you’re running nginx in front of a dev server and getting connection refused, the upstream server is probably down or on the wrong port. Check the nginx error log:
tail -f /var/log/nginx/error.logYou’ll see something like:
connect() failed (111: Connection refused) while connecting to upstreamThis means nginx is trying to reach your backend and failing. Make sure the proxy_pass URL in your nginx config matches the address and port your backend is actually running on.
Related: For more nginx troubleshooting, see Fix: nginx 502 Bad Gateway.
Dev Server Starts on a Random Port
If your dev server keeps changing ports, another process is occupying the default port and the server is auto-incrementing. Check your terminal output for the actual URL and see Fix: Port 3000 Is Already in Use to reclaim the default port.
VPN Is Interfering with Localhost
Some VPNs reroute all traffic — including localhost. If the problem started when you connected to a VPN:
- Disconnect from the VPN and test again
- If localhost works without the VPN, configure the VPN client to exclude local traffic (split tunneling)
- Try accessing
127.0.0.1directly instead oflocalhost— VPNs are more likely to interfere with DNS resolution oflocalhostthan with direct IP access
The Port Is Blocked by Another Service
On macOS, AirPlay Receiver uses port 5000. On Windows, various system services can claim common ports. Use lsof or netstat to check what’s using your port, and either stop that service or change your dev server’s port.
You’re Running the Wrong Project
If you have multiple projects, make sure you’re in the right directory and running the right dev server. Running npm run dev in the wrong folder starts the wrong server — or fails silently if there’s no dev script.
# Check where you are
pwd
# Check what dev script runs
cat package.json | grep -A2 '"dev"'Related: If your browser is blocking API requests instead of refusing the connection entirely, see Fix: Access to fetch has been blocked by CORS policy. For import resolution errors in Vite, see Fix: Vite failed to resolve import.
Related Articles
Fix: process.env.VARIABLE_NAME Is Undefined (Node.js, React, Next.js, Vite)
How to fix 'process.env.VARIABLE_NAME is undefined' and environment variables not loading from .env files in Node.js, React, Next.js, Vite, and Docker.
Fix: Port 3000 Is Already in Use (EADDRINUSE)
How to fix 'port 3000 is already in use', 'EADDRINUSE', and 'address already in use :::3000' errors in Node.js, React, Next.js, and other frameworks on macOS, Linux, and Windows.
Fix: MongoServerError: bad auth / MongoNetworkError: connect ECONNREFUSED / MongooseServerSelectionError
How to fix MongoDB 'MongoServerError: bad auth Authentication failed', 'MongoNetworkError: connect ECONNREFUSED 127.0.0.1:27017', and 'MongooseServerSelectionError' connection errors. Covers MongoDB not running, connection string format, Atlas network access, Docker networking, authentication, DNS/SRV issues, TLS/SSL, and Mongoose options.
Fix: Error: Process completed with exit code 1 (GitHub Actions)
How to fix 'Process completed with exit code 1' and other GitHub Actions workflow failures. Covers reading logs, exit codes, Node.js/Python/Docker step failures, secrets and environment variables, GITHUB_TOKEN permissions, checkout issues, caching, timeouts, self-hosted runners, matrix strategy, and artifacts.