Sửa lỗi 'Error: read ECONNRESET' Khi Thực Hiện HTTP Request Trong Node.js

intermediate💚 Node.js2026-03-25| Node.js 12+, mọi hệ điều hành (Linux/macOS/Windows), với 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

Lỗi Gặp Phải

Bạn đang thực hiện một HTTP hoặc HTTPS request và Node.js ném ra lỗi sau:

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)

Request bị hủy giữa chừng — không có status code, không có response body, chỉ là một socket bị ngắt đột ngột. Lỗi này xuất hiện với axios, module https tích hợp sẵn, node-fetch, và bất kỳ thư viện nào khác xây dựng trên tầng socket của Node.

Nguyên Nhân Thực Sự

ECONNRESET có nghĩa là máy chủ từ xa đã đóng kết nối TCP một cách bất ngờ — nó gửi gói tin RST (reset) thay vì FIN đúng quy trình. Từ góc độ của Node, kết nối vẫn còn sống rồi đột nhiên biến mất.

Có một số nguyên nhân có thể gây ra tình trạng này:

  • Máy chủ có thời gian chờ idle ngắn và kết nối của bạn nằm trong keep-alive pool quá lâu
  • Một reverse proxy (nginx, AWS ALB, Cloudflare) đã đóng kết nối trước khi Node đọc xong dữ liệu
  • Máy chủ đang quá tải và chủ động hủy bỏ các kết nối
  • Một firewall hoặc thiết bị NAT đã hủy socket đang ở trạng thái idle
  • Bạn đang tái sử dụng một socket từ connection pool mà máy chủ đã đóng trước đó
  • TLS handshake thất bại giữa chừng (ít gặp hơn, nhưng xảy ra với các cert cấu hình sai)

Cách Sửa 1: Thêm Logic Retry

ECONNRESET thường chỉ xảy ra tạm thời — một lần retry duy nhất thường là đủ để giải quyết. Dưới đây là một wrapper retry đơn giản cho axios:

const axios = require('axios');

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

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

Muốn dùng thư viện có sẵn? axios-retry xử lý điều này rất gọn gàng:

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';
  },
});

Cách Sửa 2: Tắt hoặc Tinh Chỉnh Keep-Alive

HTTP agent của Node tái sử dụng socket cho nhiều request. Vấn đề là: máy chủ có thể đã đóng socket đó trước khi request tiếp theo của bạn đến nơi. Bạn có thể tắt hoàn toàn keep-alive hoặc đặt timeout cho socket ngắn hơn thời gian idle timeout của máy chủ.

Tùy chọn A — Tắt keep-alive (cách nhanh nhất)

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));

Tùy chọn B — Giữ keep-alive nhưng đặt timeout socket ngắn hơn

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

// Hầu hết máy chủ đóng kết nối idle sau 60–120 giây.
// Đặt timeout của chúng ta là 45 giây để tái tạo socket trước khi máy chủ làm điều đó.
const agent = new https.Agent({
  keepAlive: true,
  timeout: 45000, // ms — thời gian idle timeout của socket
  maxSockets: 10,
});

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

Cách Sửa 3: Đặt Request Timeout

Một tiến trình bị treo còn tệ hơn một tiến trình bị crash. Nếu không có timeout, một socket bị reset có thể khiến ứng dụng của bạn bị treo vô thời hạn. Luôn đặt timeout:

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

// Module https tích hợp sẵn
const req = https.request(options, (res) => { /* ... */ });
req.setTimeout(10000, () => {
  req.destroy(new Error('Request timed out'));
});
req.on('error', (err) => console.error(err.message));

Cách Sửa 4: Xử Lý Sự Kiện Error Trên Raw Socket

Các request http/https thuần túy bị ECONNRESET sẽ làm crash tiến trình của bạn nếu không có error listener nào được gắn vào. Node coi các sự kiện 'error' không được xử lý là lỗi nghiêm trọng. Hãy sửa như sau:

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) => {
  // Không có đoạn này, ECONNRESET = unhandled exception = crash
  console.error('Request error:', err.code, err.message);
});

req.end();

Cách Sửa 5: Kiểm Tra Timeout của Proxy hoặc Load Balancer

Các ứng dụng chạy sau load balancer sẽ gặp thêm một vấn đề khác. AWS ALB, nginx và các proxy tương tự đều có idle timeout riêng — ALB mặc định là 60 giây. Khi proxy ngắt kết nối, ứng dụng Node của bạn sẽ nhận được lỗi ECONNRESET.

Với AWS ALB, hãy tăng idle timeout trong console: EC2 → Load Balancers → Attributes → Idle timeout. Hoặc giữ timeout của keep-alive socket dưới 60 giây như hướng dẫn ở Cách Sửa 2.

Với nginx đóng vai trò reverse proxy:

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

Xác Nhận Đã Sửa Xong

  • Thêm một dòng log trong khối catch: console.log('err.code:', err.code). Sau khi sửa, bạn sẽ không còn thấy ECONNRESET nữa.
  • Chạy request trong vòng lặp liên tục 30–60 giây để xác nhận socket được tái tạo sạch sẽ:
setInterval(() => {
client.get('https://api.example.com/ping')
  .then(() => console.log('OK'))
  .catch(err => console.error(err.code));
}, 5000);

  • Theo dõi netstat hoặc ss để xác nhận không còn socket nào bị kẹt ở trạng thái CLOSE_WAIT:
watch -n 2 'ss -tan | grep CLOSE_WAIT | wc -l'

Mẹo Thêm

  • Log đúng các trường cần thiết: Luôn ghi lại err.code, err.addresserr.port — chúng cho bạn biết chính xác host nào đã reset kết nối, giúp tiết kiệm rất nhiều thời gian phán đoán.
  • Debug mạng: Nếu bạn nghi ngờ vấn đề nằm ở định tuyến subnet hoặc firewall rules giữa ứng dụng Node và máy chủ đích, Subnet Calculator của ToolCraft giúp xác minh nhanh các dải CIDR và địa chỉ mạng — rất tiện khi ứng dụng chạy trong VPC hoặc mạng nội bộ doanh nghiệp.
  • Vấn đề TLS: ECONNRESET chỉ xảy ra trên HTTPS? Hãy kiểm tra các certificate đã hết hạn hoặc self-signed. Chạy NODE_TLS_REJECT_UNAUTHORIZED=0 tạm thời (không bao giờ dùng trên môi trường production) để xác nhận TLS có phải là nguyên nhân không.
  • Xử lý đồng thời: Giá trị maxSockets cao có thể làm quá tải máy chủ với nhiều request song song. Hãy bắt đầu với 10 rồi điều chỉnh dần.

Tóm Tắt

  • Thêm logic retry với exponential backoff — khắc phục hầu hết các lỗi reset tạm thời
  • Tắt keep-alive hoặc đặt socket timeout ngắn hơn idle timeout của máy chủ
  • Luôn đặt request timeout để tránh tiến trình bị treo khi gặp reset
  • Luôn gắn .on('error', ...) vào các HTTP/HTTPS request thuần túy
  • Chạy sau proxy? Hãy đồng bộ timeout giữa Node và tầng proxy

Related Error Notes