Skip to content

ECONNRESET through a proxy: find which side reset

ECONNRESET means the other end of a TCP connection aborted it. Through a proxy, the other end of your socket is always the proxy, so the reset can start at the proxy, at the exit or at the website and still look the same. The most common causes are reusing a keep-alive connection the other side already closed, and the wrong protocol for the proxy port. Retry requests that are safe to repeat, and pair the proxy URL's scheme with its port.

Reviewed by John Hale. Last updated .

What it looks like

Node.js
Error: read ECONNRESET            (code: 'ECONNRESET', syscall: 'read')
Error: socket hang up             (code: 'ECONNRESET')
Error: Client network socket disconnected before secure TLS connection was established

Python requests
requests.exceptions.ConnectionError: ('Connection aborted.', ConnectionResetError(104, 'Connection reset by peer'))

curl
curl: (56) Recv failure: Connection reset by peer
curl: (35) Recv failure: Connection reset by peer

What does ECONNRESET mean through a proxy?

A reset is a TCP segment with the RST flag. Unlike a normal close, it says the connection is gone now and any unread data is discarded. Node.js reports it as error.code === "ECONNRESET"; Python raises ConnectionResetError, errno 104 on Linux, 54 on macOS and 10054 on Windows.

With a proxy there are two connections: yours to the proxy, and the proxy's to the site. When the site or the exit resets its side, the proxy usually closes or resets yours, so the error looks the same wherever it started.

When it happens tells you most. A reset before any answer means the proxy refused the connection. A reset during the TLS handshake points at a protocol mismatch or a site that refused the client. A reset mid-download, or on the first request after an idle pause, points at a connection that someone closed.

What causes ECONNRESET through a proxy?

Most likely first.

  1. A reused keep-alive connection was already closed. Connection pools keep idle sockets for the next request. If the proxy or the site closed one after its own idle timeout, the next request goes out on a dead socket and gets a reset. This is the usual cause of occasional resets in long-running jobs.
  2. The wrong protocol for the port. An https:// proxy URL sends TLS to the proxy; on a port that expects plain HTTP the handshake fails or is reset. urllib3 recognises the case and says Your proxy appears to only use HTTP and not HTTPS. Sending HTTP to a SOCKS5 port fails the same way.
  3. The proxy refused and closed. Many proxies close the connection after an error response such as 407. A client that keeps writing to it, or reuses it, sees a reset or socket hang up instead of the status.
  4. The website reset the connection. Some sites reset connections they refuse, often during the TLS handshake or after too many requests.
  5. The exit went away mid-transfer. A residential exit is a household connection that can drop at any time. Long downloads and long-lived connections are the ones that notice.
  6. Something on your side. NAT gateways and firewalls drop connections that stay idle, which breaks WebSockets and long polls. Antivirus HTTPS scanning, VPN clients and company TLS inspection can reset connections they decide to block.

How do you find which side reset?

  1. Reproduce with curl to see the stage

    (56) with no answer to CONNECT: the proxy reset before replying. (35) after 200 Connection established: the reset came during TLS with the site. (56) after data arrived: the transfer was cut.

    Watch the stages
    curl -v -x http://HOST:PORT -U "USER:PASS" https://TARGET/ -o /dev/null

  2. Check the scheme against the port

    The proxy URL's scheme describes the connection to the proxy, not to the site, so it is http:// for a plain proxy port even when the site is https://.

    Plain HTTP and TLS to the proxy
    # Plain HTTP to the proxy
    curl -v -x http://HOST:HTTP_PORT -U "USER:PASS" https://example.com/ -o /dev/null
    # TLS to the proxy, only on a port that offers it
    curl -v -x https://HOST:HTTPS_PORT -U "USER:PASS" https://example.com/ -o /dev/null

  3. Turn off connection reuse for one run

    If the resets stop with keep-alive off, stale pooled sockets were the cause. In Node.js, create the agent with keepAlive: false; in Python, send Connection: close. With logging.basicConfig(level=logging.DEBUG), urllib3 logs Resetting dropped connection when it finds a pooled socket closed.

    Python: one connection per request
    r = requests.get(url, proxies=proxies, headers={"Connection": "close"}, timeout=30)

  4. Note the idle time

    Log how long each connection was open and idle when it failed. Resets at the same idle age point at an idle timeout somewhere on the path. NODE_DEBUG=net,tls node app.js prints socket events in Node.js.

How do you fix ECONNRESET?

  • Retry requests that are safe to repeat

    A reset on a reused socket almost always succeeds on a fresh one, so retry once or twice with a short backoff.

    Python requests with urllib3 retries
    import requests
    from requests.adapters import HTTPAdapter
    from urllib3.util.retry import Retry
    
    proxy = "http://USER:PASS@HOST:PORT"
    retry = Retry(total=3, connect=2, read=2, backoff_factor=0.5,
                  allowed_methods=["GET", "HEAD", "OPTIONS"])
    session = requests.Session()
    session.mount("http://", HTTPAdapter(max_retries=retry))
    session.mount("https://", HTTPAdapter(max_retries=retry))
    session.proxies = {"http": proxy, "https": proxy}
    
    r = session.get("https://example.com/", timeout=(10, 60))

  • The same in Node.js

    If resets follow idle pauses, close idle sockets sooner than the far side does, or create the agent with keepAlive: false.

    Node.js with https-proxy-agent
    import https from "node:https";
    import { HttpsProxyAgent } from "https-proxy-agent";
    
    const agent = new HttpsProxyAgent("http://USER:PASS@HOST:PORT", { keepAlive: true });
    
    function get(url, attempt = 0) {
      return new Promise((resolve, reject) => {
        https.get(url, { agent }, resolve).on("error", (err) => {
          if (err.code === "ECONNRESET" && attempt < 2) {
            setTimeout(() => get(url, attempt + 1).then(resolve, reject), 500 * (attempt + 1));
          } else {
            reject(err);
          }
        });
      });
    }

  • Fix the setup

    Use http:// for a plain HTTP proxy port, https:// only on a port that offers TLS to the proxy, and socks5h:// only on a SOCKS5 port. Log error responses such as 407 before your client discards them. For WebSockets and other long-lived connections, send pings more often than the shortest idle timeout on the path, and reconnect when they stop.

If you use TrueProxies

The gateway listens for plain HTTP on 8080, TLS to the proxy on 8443 and SOCKS5 on 1080. Use http://HOST:8080 for both http:// and https:// sites, https://HOST:8443 when your client supports TLS to the proxy, and socks5h://HOST:1080.

After a refusal the gateway answers 407 with a reason and closes the connection. Clients that do not read the answer report the close as ECONNRESET or socket hang up; send one request with curl -v to read X-Proxy-Reason.

Usage history on the service page records which side ended a connection. Connection reset is a reset recorded on the proxy's side of the route. Reset on your side, listed under Ended on your side, is one observed on your side, and does not count against connection success.

A residential exit can go offline in the middle of a connection. A sticky session then gets a new IP; keep the session ID and retry.

Test the proxy from outside your network

The free proxy checker connects from our servers and reports the failed stage as a code, so you can tell a proxy problem from one on your computer or network.

Run the proxy checker

Questions about ECONNRESET through a proxy

Is ECONNRESET a proxy error or a website error?

Either. The proxy passes a reset from the exit or the site on to you as a reset or a close. The stage at which it happens, and curl -v, show which side started it.

What is the difference between ECONNRESET and socket hang up?

Both carry code ECONNRESET in Node.js. socket hang up means the connection closed before a response arrived; read ECONNRESET means a reset arrived while Node.js was reading.

Why does ECONNRESET happen only sometimes?

Occasional resets on reused connections are the classic sign of keep-alive sockets closed by the far side while idle. Retry once on a fresh connection, and close idle sockets sooner.

Should I retry POST requests after ECONNRESET?

Only if the request is safe to repeat. The server may have processed it before the reset. Use an idempotency key where the API supports one.