ECONNREFUSED と ETIMEDOUT の違い・切り分け
どちらも「接続できない」ように見えますが、発生レイヤが違います。違いを押さえると 診断時間が一桁縮みます。
Both look like "cannot connect", but they happen at different TCP stages. Knowing which means which cuts your triage time drastically.
TL;DR
ECONNREFUSED= ポートは閉じている(RST即答)。ホストまでは届いているETIMEDOUT= 応答なし(パケット落ち)。ホストまで届いていない可能性が高い- 前者はアプリ/ポート、後者はネットワーク/ファイアウォールを疑う
1. TCP レベルで何が起きているか / At the TCP layer
| エラー / Error | TCP 動作 | 発生時間 |
|---|---|---|
ECONNREFUSED | SYN → RST (リセット) | 即時 (数ms) |
ETIMEDOUT | SYN → 応答なし | OS 既定で 21〜75秒 |
2. 切り分けコマンド / Diagnose
# 1. DNS 解決できるか
nslookup example.com
# 2. TCPで到達できるか
nc -vz example.com 443 # Succeeded = 到達、 timed out = ETIMEDOUT相当
curl -v https://example.com
# 3. ローカルの listen 確認
ss -ltnp | grep 3000 # Linux
lsof -i :3000 # macOS3. よくある原因 / Common causes
| エラー | 原因 | 対処 |
|---|---|---|
ECONNREFUSED 127.0.0.1:3000 | サーバ未起動 / 別IFにバインド | listen 確認、0.0.0.0 にバインド |
ECONNREFUSED (Docker) | コンテナ名/ネットワーク誤り | depends_on とサービス名で接続 |
ETIMEDOUT (外部API) | ファイアウォール / egress制限 | VPC の outbound rule を確認 |
ETIMEDOUT (Lambda) | VPC 設定のNAT欠落 | NAT Gateway / VPC Endpoint 追加 |
4. Node.js 側でのハンドリング / Handling in Node.js
try {
await fetch("https://api.example.com", { signal: AbortSignal.timeout(5000) });
} catch (e) {
if (e.code === "ECONNREFUSED") console.error("port closed");
else if (e.code === "ETIMEDOUT" || e.name === "TimeoutError") console.error("no response");
else throw e;
}5. English summary
ECONNREFUSED means the TCP stack on the target host replied with RST — the host is reachable but nothing is listening on that port. ETIMEDOUT means no reply came back at all, usually a firewall silently dropping packets or a network partition. Diagnose in this order: DNS → nc -vz host port → local listen check. For Lambda and container environments, ETIMEDOUT often points to missing NAT or wrong service-to-service networking.