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.cause?.code === "ECONNREFUSED") console.error("port closed");
else if (e.cause?.code === "ETIMEDOUT" || e.name === "TimeoutError") console.error("no response");
else throw e;
}実際に検証してみると / What we found when we actually tested this
上のコード例が e.cause?.code を確認しているのには理由があります。実際に Node.js v24.14.1 で、誰も listen していないポートに対して fetch("http://127.0.0.1:54321", { signal: AbortSignal.timeout(3000) }) を実行したところ、e.code は undefined のままで、実際のエラーコード "ECONNREFUSED" は e.cause.code に入っていました。
name: TypeError
message: fetch failed
code: undefined
cause code: ECONNREFUSED
cause: Error: connect ECONNREFUSED 127.0.0.1:54321これは Node.js の fetch が undici 実装をベースにしており、低レベルのネットワークエラー情報を cause プロパティに格納する設計になっているためです。
There is a reason the example above checks e.cause?.code: in our Node.js v24.14.1 test, e.code remained undefined, while the actual "ECONNREFUSED" code appeared in e.cause.code. This is because Node.js fetch is based on undici, which stores low-level network error details in the cause property.
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.
よくある質問 / FAQ
- ECONNREFUSED と ETIMEDOUT の違いは?
- ECONNREFUSED は宛先ホストまで届いたがポートが開いていない(即時 RST 応答)。ETIMEDOUT は宛先から応答が一切ない(ファイアウォール遮断や経路断、オフライン)を意味します。
- What's the difference between ECONNREFUSED and ETIMEDOUT?
- ECONNREFUSED means the host responded but the port is closed (TCP RST). ETIMEDOUT means no response at all — typically a firewall dropping packets or a network partition.
- localhost:3000 で ECONNREFUSED
- サーバがそのポートで listen していないか、別IF(::1 vs 127.0.0.1)にバインドしています。listen 状況を lsof -i :3000 などで確認してください。