Reference

ERR_CONNECTION_REFUSED: what it means and how to fix it

6 min read · Published Jul 17, 2026
Contents · 6 sections
  1. What "refused" means at the TCP level
  2. Server-side causes
  3. Client-side causes
  4. The localhost case, for developers
  5. Down for everyone, or refused just for you
  6. FAQ

ERR_CONNECTION_REFUSED is Chrome's way of reporting that the TCP connection itself was rejected. Your machine resolved the domain to an IP address, sent a connection request to that IP, and got an active "no" back before any HTTP happened. That makes it different from a timeout, which is silence, and from NXDOMAIN, which means the name never resolved at all. Something at that address is alive and answering, it just refused to talk to you on that port.

What "refused" means at the TCP level

Every HTTP request starts with a TCP handshake. Your client sends a SYN packet to the server's IP and port, and one of three things comes back:

Two things commonly produce that reset. First, nothing is listening: the machine is up but no process has the port bound, so the kernel itself replies with an RST. Second, a firewall configured to REJECT rather than DROP: a REJECT rule sends the reset (or an ICMP unreachable) back explicitly, while a DROP rule discards the packet silently and produces a timeout.

ErrorStage that failedWhat actually happened
DNS_PROBE_FINISHED_NXDOMAINName resolutionThe domain never resolved to an IP address
ERR_CONNECTION_REFUSEDTCP connectThe IP replied with a reset, an active rejection
ERR_CONNECTION_TIMED_OUTTCP connectPackets went unanswered, silence

Refusal is the noisiest of the three failures and the most informative. It arrives in milliseconds rather than after a 20-30 second wait, and it proves a machine at that IP is powered on and routable. The only question is why it said no. If your failure is at the name-resolution stage instead, see DNS_PROBE_FINISHED_NXDOMAIN.

Server-side causes

Client-side causes

If the site is fine for everyone else, the refusal is being generated on or near your machine. The giveaway is usually that many sites fail at once, because it is unlikely every server you visit crashed simultaneously.

The localhost case, for developers

The most common place to meet this error is http://localhost:3000 during development. There is no mystery here: refused on localhost means nothing on your machine is accepting connections on that port. The usual suspects, in order:

  1. The dev server isn't running. It never started, or it crashed on boot and the error scrolled past. Check the terminal it was started in.
  2. It's on a different port. Many dev servers bump to 3001 or another port when the default is taken, and print the new port at startup.
  3. IPv4 vs IPv6 mismatch. On some systems localhost resolves to the IPv6 address ::1 first. If the server bound only to the IPv4 127.0.0.1, the browser connects to ::1, finds nothing, and reports refusal. Browsing to 127.0.0.1:3000 directly is the test.
  4. Docker port mapping. The container port isn't published, or the app inside the container bound to 127.0.0.1, which is the container's own loopback, so the published port connects to nothing. Bind to 0.0.0.0 inside containers.

To see what is actually listening, run ss -ltn or lsof -i :3000 on Linux and macOS, or netstat -ano on Windows, then compare the bound address and port against what the browser is dialing.

Down for everyone, or refused just for you

Because refusal happens at the TCP layer, before TLS and before any HTTP request is sent, it produces an unusually clean signal on a multi-method check. isitdown.io probes a target with 4 independent probe methods in parallel, HTTP/1.1 with strict TLS, HTTP/2 with relaxed TLS, a GET with a real browser User-Agent, and a HEAD with a curl User-Agent. All four have to complete the same TCP handshake first, so a genuine connection refusal at the origin fails all four identically.

After the check, confirm from a second network, a mobile hotspot is the quickest. If you operate the site yourself, a free isitdown.io account monitors up to 5 sites with the same four methods every 5 minutes and emails you on failure. An incident only opens after 2 consecutive failed probes, so a few seconds of refusals during a deploy won't page you, and it resolves after 2 consecutive passes.

FAQ

Does ERR_CONNECTION_REFUSED mean the server is offline?

No, usually the opposite. A powered-off or unreachable host produces a timeout, because nothing is there to answer. A refusal means something at that IP, the kernel or a firewall, is alive and actively sending resets. The machine is up; the service on that port is not, or is being shielded.

Why am I getting it on every website at once?

Every site failing simultaneously points to a broken local intermediary: a system proxy that isn't running, a VPN that dropped with a kill switch active, or an antivirus web-protection module that crashed. It is not plausible that every origin server refused you at the same moment.

My server says it's running, so why is localhost refused?

A live process is not the same as a process listening where you are connecting. Check the bound address and port with ss -ltn or lsof. The two classic mismatches are the server on 127.0.0.1 while the browser connects to the IPv6 ::1, and a Docker app bound to the container's loopback so the published port maps to nothing.

Can a firewall cause refused instead of timed out?

Yes. A firewall rule set to REJECT sends an explicit reset or ICMP unreachable, which surfaces as refused. A rule set to DROP discards packets silently, which surfaces as a timeout. Which error you see tells you how the blocking device was configured.

Share 𝕏 Twitter LinkedIn
Keep reading

← All notes & guides