Fix 'Error: read ECONNRESET' When Making HTTP Requests in Node.js

intermediate๐Ÿ’š Node.js2026-03-25| Node.js 12+, any OS (Linux/macOS/Windows), with http/https/axios/node-fetch

Error Message

Error: read ECONNRESET at TLSSocket.onConnectEnd (_tls_wrap.js:1495:19) at Object.onceWrapper (events.js:422:26) at TLSSocket.emit (events.js:327:22)
#nodejs#http#network#econnreset#socket#axios

The Error

You're making an HTTP or HTTPS request and Node.js throws this:

Error: read ECONNRESET
    at TLSSocket.onConnectEnd (_tls_wrap.js:1495:19)
    at Object.onceWrapper (events.js:422:26)
    at TLSSocket.emit (events.js:327:22)

The request dies mid-flight โ€” no status code, no response body, just a broken socket. It hits axios, the built-in https module, node-fetch, and anything else sitting on top of Node's socket layer.

What's Actually Happening

ECONNRESET means the remote server closed the TCP connection unexpectedly โ€” it sent a RST (reset) packet instead of a proper FIN. From Node's perspective, the connection was alive and then suddenly gone.

Several things can trigger this:

  • The server has a short idle timeout and your connection sat in a keep-alive pool too long
  • A reverse proxy (nginx, AWS ALB, Cloudflare) closed the connection before Node finished reading
  • The server is under load and actively dropping connections
  • A firewall or NAT device killed an idle socket
  • You're reusing a socket from a connection pool that the server already closed
  • TLS handshake failed mid-stream (less common, but happens with misconfigured certs)

Fix 1: Add Retry Logic

ECONNRESET is usually transient โ€” a single retry fixes it most of the time. Here's a simple retry wrapper for axios:

const axios = require('axios');

async function requestWithRetry(url, options = {}, retries = 3) {
  for (let attempt = 1; attempt  setTimeout(r, delay));
        continue;
      }
      throw err;
    }
  }
}

// Usage
requestWithRetry('https://api.example.com/data')
  .then(res => console.log(res.data))
  .catch(err => console.error('All retries failed:', err.message));

Prefer a library? axios-retry handles this cleanly:

npm install axios-retry
const axios = require('axios');
const axiosRetry = require('axios-retry').default;

axiosRetry(axios, {
  retries: 3,
  retryDelay: axiosRetry.exponentialDelay,
  retryCondition: (error) => {
    return axiosRetry.isNetworkError(error) || error.code === 'ECONNRESET';
  },
});

Fix 2: Disable or Tune Keep-Alive

Node's HTTP agent reuses sockets across requests. The problem: the server may have already closed that socket by the time your next request arrives. You can either kill keep-alive entirely or set a socket timeout that's shorter than the server's idle timeout.

Option A โ€” Disable keep-alive (quickest fix)

const https = require('https');
const axios = require('axios');

const agent = new https.Agent({ keepAlive: false });

const client = axios.create({ httpsAgent: agent });

client.get('https://api.example.com/data')
  .then(res => console.log(res.data));

Option B โ€” Keep keep-alive but set a shorter socket timeout

const https = require('https');
const axios = require('axios');

// Most servers close idle connections after 60โ€“120s.
// Set our timeout to 45s to recycle before the server does.
const agent = new https.Agent({
  keepAlive: true,
  timeout: 45000, // ms โ€” socket idle timeout
  maxSockets: 10,
});

const client = axios.create({
  httpsAgent: agent,
  timeout: 30000, // request timeout
});

Fix 3: Set a Request Timeout

A hanging process is worse than a crashed one. Without a timeout, a reset socket can stall your app indefinitely. Always set one:

// axios
const client = axios.create({ timeout: 10000 }); // 10 seconds

// Built-in https module
const req = https.request(options, (res) => { /* ... */ });
req.setTimeout(10000, () => {
  req.destroy(new Error('Request timed out'));
});
req.on('error', (err) => console.error(err.message));

Fix 4: Handle the Error Event on Raw Sockets

Raw http/https requests that throw ECONNRESET will crash your process if no error listener is attached. Node treats unhandled 'error' events as fatal. Fix it like this:

const https = require('https');

const req = https.request('https://api.example.com/data', (res) => {
  let body = '';
  res.on('data', chunk => body += chunk);
  res.on('end', () => console.log(body));
});

req.on('error', (err) => {
  // Without this, ECONNRESET = unhandled exception = crash
  console.error('Request error:', err.code, err.message);
});

req.end();

Fix 5: Check for Proxy or Load Balancer Timeouts

Apps behind a load balancer face an extra wrinkle. AWS ALB, nginx, and similar proxies each have their own idle timeout โ€” ALB defaults to 60 seconds. When the proxy drops the connection, your Node app sees an ECONNRESET.

For AWS ALB, bump the idle timeout in the console: EC2 โ†’ Load Balancers โ†’ Attributes โ†’ Idle timeout. Or keep your keep-alive socket timeout under 60s as shown in Fix 2.

For nginx acting as a reverse proxy:

# nginx.conf
proxy_read_timeout 120s;
proxy_send_timeout 120s;
keepalive_timeout 75s;

Verify the Fix

  • Add a log line in your catch block: console.log('err.code:', err.code). After the fix, you should stop seeing ECONNRESET.
  • Run your request in a loop for 30โ€“60 seconds to confirm sockets are being recycled cleanly:
setInterval(() => {
client.get('https://api.example.com/ping')
  .then(() => console.log('OK'))
  .catch(err => console.error(err.code));
}, 5000);

  • Watch netstat or ss to confirm no lingering sockets in CLOSE_WAIT:
watch -n 2 'ss -tan | grep CLOSE_WAIT | wc -l'

Tips

  • Log the right fields: Always capture err.code, err.address, and err.port โ€” they tell you exactly which host reset the connection, which saves a lot of guessing.
  • Network debugging: If you suspect the issue is subnet routing or firewall rules between your Node app and the target server, ToolCraft's Subnet Calculator helps verify CIDR ranges and network addresses quickly โ€” handy when your app runs inside a VPC or corporate network.
  • TLS issues: ECONNRESET on HTTPS only? Check for expired or self-signed certificates. Run NODE_TLS_REJECT_UNAUTHORIZED=0 temporarily (never in production) to confirm TLS is the culprit.
  • Concurrency: High maxSockets can overwhelm the server with parallel requests. Start at 10 and tune from there.

Summary

  • Add retry logic with exponential backoff โ€” fixes most transient resets
  • Disable keep-alive or set socket timeout shorter than the server's idle timeout
  • Always set a request timeout so resets don't stall your process
  • Always attach .on('error', ...) on raw HTTP/HTTPS requests
  • Behind a proxy? Align timeouts between Node and the proxy layer

Related Error Notes