How to Fix ECONNREFUSED (Connection Refused)
Learn which machine refused you from the message, then apply three fixes, one with cURL, Python and Node.js code.
Updated
TL;DR
ECONNREFUSED means the host at the ip:port in the message rejected the connection because no program there accepts it; when your client uses an HTTP proxy, that host is always the proxy, never the site. Start the service, or point your code at the host and port it listens on.
What ECONNREFUSED looks like in each client
Where a line shows an address and port, it is the one that client tried.
| Where | What you see |
|---|---|
| Node.js http module | Error: connect ECONNREFUSED 127.0.0.1:8080 |
| Node.js fetch (undici) | TypeError: fetch failed ... [cause]: Error: connect ECONNREFUSED 127.0.0.1:8080 |
| axios, as console.log prints it | AxiosError: connect ECONNREFUSED 127.0.0.1:8080 |
| Node.js 20 or later, host name localhost | AggregateError [ECONNREFUSED]: |
| Vite dev server, server.proxy target down | [vite] http proxy error: /api/users |
| Python requests on macOS (urllib3 2.6 or later) | requests.exceptions.ConnectionError: HTTPConnectionPool(host='localhost', port=8080): Max retries exceeded with url: / (Caused by NewConnectionError("HTTPConnection(host='localhost', port=8080): Failed to establish a new connection: [Errno 61] Connection refused")) |
| curl | curl: (7) Failed to connect to localhost port 8080 after 0 ms: Couldn't connect to server |
Why this happens
Nothing on that machine was listening on the port your code tried.
Most often the server has not started or has crashed, so its port is closed and the host answers your connection request with a TCP reset.
Diagnose your ECONNREFUSED first
These checks tell you which machine refused and why; the first one needs only the error text.
Read the address after ECONNREFUSED; 127.0.0.1 or ::1 means the closed port is on your own computer, and any other means a remote one.
Run lsof -nP -iTCP:8080 -sTCP:LISTEN, or netstat -ano | findstr :8080 on Windows; no output means nothing listens on that port.
Compare the address in that lsof line with the error's; if one is [::1] and the other 127.0.0.1, client and server disagree.
If the error names a port you never set, run env | grep -i proxy; a leftover proxy variable sends curl, axios and Python there.
Inside a container, run the same request from your computer; if only the container fails, localhost there is the container's own loopback.
Solutions ranked by effectiveness
Most refusals end with the first fix; the other two cover address mismatches and slow-starting services.
- Most common fix
Start the service on the port you call
Applies when the lsof check comes back empty. Start the program and copy the port it prints at startup, which can differ from the one in your config.
Start the server or database, then wait for the line that names its port.
Copy that exact port into your client URL, proxy target or connection string.
If the program exits at once, fix the error its log shows first.
For a proxy, paste its host and port again from your provider's dashboard.
- Check next
Match the client to the server's address
Applies when a server is running, but bound to an IP your client does not dial, for example IPv6 ::1 versus IPv4 127.0.0.1.
If the server listens on [::1] only, use the name localhost, not 127.0.0.1.
Or pin the server to one address, for example vite --host 127.0.0.1.
To accept other machines on a trusted network, bind it to 0.0.0.0 instead.
From a container, call services by name (db:5432) and Docker Desktop's host at host.docker.internal.
- For developers
Retry only while the service starts
Applies when tests or a CI job call a database or API before it accepts connections. Retry refusals only, with a pause and a cap, then stop with a clear error.
curl --retry 10 --retry-delay 2 --retry-connrefused \ -sS -o /dev/null -w "HTTP %{http_code}\n" \ http://127.0.0.1:8080/
Stop the ECONNREFUSED from coming back
Habits that keep ports, addresses and startup order predictable, so clients find the listener they expect.
Set server.strictPort: true in vite.config, so Vite exits on a taken port and you notice.
Read the port from one environment variable in both the server and every client config.
In Docker Compose, give databases a healthcheck and set depends_on to condition: service_healthy.
Wherever a proxy variable is set, add NO_PROXY=localhost,127.0.0.1 so local calls skip the proxy.
Give long-running containers a restart policy such as on-failure, so a crash does not leave the port closed.
Related errors
Learn more
FAQ
Proxy URLs with no login inside
IP whitelisting lets HTTP_PROXY name only a Geonode gateway's host and port.


