ERR_CONNECTION_REFUSED の原因と対処
ERR_CONNECTION_REFUSED は、TCP接続がRST(リセット)で即座に拒否されたときにブラウザが表示するエラーです。宛先ホストまでパケットは届いているものの、指定したポートで誰も待ち受けていないのが最も多い原因です。開発中は localhostへの接続でこのエラーが出るケースが大半なので、本記事ではlocalhost開発時の 切り分けを中心に整理します。
ERR_CONNECTION_REFUSED means the destination host actively rejected the TCP connection with a RST — the packet arrived, but nothing was listening on that port. This guide focuses on diagnosing it during local development.
1. ERR_CONNECTION_TIMED_OUTとの違い
名前が似ているエラーとして ERR_CONNECTION_TIMED_OUT があります。両者は 発生の仕組みが異なります。
| エラー | TCPの動き | 発生までの時間 | 意味 |
|---|---|---|---|
ERR_CONNECTION_REFUSED | SYN → RST | 即時(数ms) | ホストには届いたがポートが閉じている |
ERR_CONNECTION_TIMED_OUT | SYN → 応答なし | OS既定で数十秒 | パケットが届いていない可能性が高い |
TCPレベルでの詳しい違いと切り分けコマンドはECONNREFUSED と ETIMEDOUT の違い・切り分けにまとめています。
2. localhost開発での切り分けチェックリスト
(a) devサーバーが本当に起動しているか
最も多い原因です。ターミナルでプロセスが実際に稼働しているか、起動直後にクラッシュ していないかを確認してください。npm run dev 等を実行したターミナルに エラーで落ちた形跡がないかをまず見ます。
(b) ポート番号の不一致
サーバーは3000番で起動しているのに、ブラウザで8080番を開いている、といった単純な 指定違いも頻出します。サーバー起動時のログに出るポート番号と、アクセスしているURLの ポート番号を突き合わせてください。
(c) バインド先の問題
サーバーが 127.0.0.1 など特定のIPアドレスだけにバインドしていて、 別のインターフェースからの接続を受け付けていないケースです。特にDocker/WSL2の コンテナ内で動かしている場合、コンテナ内のサーバーが 0.0.0.0 でなくlocalhost にバインドしていると、ホスト側からポートフォワーディング 経由でアクセスしても接続が拒否されます。サーバーの起動オプションで待ち受けアドレスを0.0.0.0 に変更できるか確認してください。
(d) IPv4/IPv6の食い違い
localhost という名前解決の結果がIPv6の ::1 になり、 サーバー側はIPv4の 127.0.0.1 だけで待ち受けている場合に接続が拒否される ことがあります。Node.js 17以降はDNS解決の既定順序が変更され、環境によってlocalhost がIPv6優先で解決されるようになったため、以前は問題なかった 構成でこの事象が新たに出ることがあります。対処法は、サーバー側を:: (IPv4/IPv6両対応)でバインドするか、アクセス側のURLを127.0.0.1 に明示的に置き換えることです。
(e) ファイアウォール・VPN・プロキシ
ローカルのファイアウォールや常駐セキュリティソフトが特定のポートをブロックしている 場合もあります。ブラウザ側でプロキシ設定が有効になっており、そのプロキシ自体が 落ちている場合は ERR_PROXY_CONNECTION_FAILED という別のエラーコードに なるため、まずエラーコードそのものが ERR_CONNECTION_REFUSED かどうかを 確認し、プロキシ由来かどうかを切り分けてください。
3. 確認コマンド実例
まずサーバーが実際にそのポートで LISTENING 状態になっているかを OSごとのコマンドで確認します。
Windows (netstat)
netstat -ano | findstr LISTENING | findstr :3000典型的な出力(該当ポートがLISTENING状態の場合):
TCP 0.0.0.0:3000 0.0.0.0:0 LISTENING 123451列目がプロトコル、2列目がローカルアドレス、最後がPIDです。この行が出てこない場合、そのポートでは誰も待ち受けていません。0.0.0.0:3000 ではなく 127.0.0.1:3000 とだけ表示されている 場合は、外部インターフェースからは接続できないバインド設定になっています。
macOS / Linux (lsof)
lsof -i :3000COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
node 12345 asahi 20u IPv6 0x... 0t0 TCP *:3000 (LISTEN)疎通確認 (curl)
curl -v http://localhost:3000/接続が拒否される場合の典型的な出力:
* Trying 127.0.0.1:3000...
* connect to 127.0.0.1 port 3000 failed: Connection refused
* Failed to connect to localhost port 3000 after 2 msポート単位の疎通確認 (telnet / Test-NetConnection)
# macOS / Linux
telnet localhost 3000
# Windows PowerShell
Test-NetConnection -ComputerName localhost -Port 3000Test-NetConnection は結果に TcpTestSucceeded : False と 表示された場合、そのポートへの接続が失敗していることを意味します。
4. Node.jsから見た同じ事象
ブラウザの ERR_CONNECTION_REFUSED に対応する事象は、Node.jsの組み込み fetchでは TypeError: fetch failed となり、詳細は err.causeに入ります。
await fetch("http://127.0.0.1:3000/");
// TypeError: fetch failed
// cause: Error: connect ECONNREFUSED 127.0.0.1:3000
// code: 'ECONNREFUSED'ECONNREFUSED と、応答が一切ない ETIMEDOUT の違いはECONNREFUSED と ETIMEDOUT の違い・切り分けで詳しく解説しています。
5. Docker compose内の定番の落とし穴
Docker composeで複数サービスを起動している場合、コンテナ同士はlocalhostでは なくサービス名で繋ぐのが定番のルールです。localhost はコンテナ 自身を指すため、別コンテナで動いているサービスには到達できずERR_CONNECTION_REFUSED(またはNode.js側ではECONNREFUSED)に なります。
# NG: コンテナ内からlocalhostで別サービスを呼ぶ
fetch("http://localhost:5432/")
# OK: docker-compose.ymlのサービス名で呼ぶ
fetch("http://db:5432/")6. 一般サイト閲覧時に出る場合
開発とは無関係に、通常のWebサイト閲覧中にこのエラーが出ることもあります。原因は 大きく分けて次のいずれかです。
- サイト側のサーバーが停止している、または特定のポートへの接続を受け付けていない
- 自宅のルーター・ISP側の障害や設定変更でその宛先への経路が遮断されている
- ローカルのウイルス対策ソフト・ファイアウォールが該当の通信をブロックしている
簡易的な切り分けとして、同じURLを別の端末や別のネットワーク(スマートフォンの モバイル回線など)から開いてみて、同じエラーが出るかどうかを確認してください。 他の環境からも同様に拒否される場合はサイト側の問題である可能性が高くなります。
7. English summary
ERR_CONNECTION_REFUSEDmeans the TCP connection was actively rejected with a RST — the destination host was reached, but nothing was listening on the requested port. During local development the most common causes are: the dev server isn't actually running or crashed, a port number mismatch between the server and the URL you opened, the server binding only to a specific interface (a common trap inside Docker/WSL2 where the process binds to localhost instead of 0.0.0.0), and an IPv4/IPv6 mismatch where localhost resolves to ::1 but the server only listens on 127.0.0.1 (relevant since Node.js 17 changed default DNS resolution order). Verify with netstat -ano | findstr LISTENING (Windows) orlsof -i :PORT (macOS/Linux) to confirm the port is actually listening, and withcurl -v or Test-NetConnection to test reachability. Inside Docker compose, connect services by their service name, not localhost.
よくある質問 / FAQ
- ERR_CONNECTION_REFUSEDとは?
- 接続先のホストにTCPパケットは届いたが、指定したポートで待ち受けているプロセスが存在しないため、即座にRST(接続拒否)が返された状態です。宛先に到達していない ERR_CONNECTION_TIMED_OUT とは原因のレイヤが異なります。
- ERR_CONNECTION_REFUSEDとERR_CONNECTION_TIMED_OUTの違いは?
- REFUSEDはホストまで届いてポートが閉じていることが即座にわかる状態(数ms)、TIMED_OUTはパケットへの応答が一切なく、OS既定のタイムアウト秒数(数十秒)待ってから失敗する状態です。ファイアウォールでの遮断は多くの場合TIMED_OUTになります。
- localhostで開発中にERR_CONNECTION_REFUSEDが出る
- 最も多いのはdevサーバーが起動していない・クラッシュしている、またはポート番号の指定違いです。次に多いのがバインド先の問題で、Docker/WSL2内で0.0.0.0でなくlocalhostにバインドしているとホスト側から接続できません。
- Node.jsで同じ事象が起きるとどう表示される?
- TypeError: fetch failed となり、err.cause に connect ECONNREFUSED 127.0.0.1:PORT という詳細が入ります。ブラウザのERR_CONNECTION_REFUSEDと対応する事象です。
- Docker composeのコンテナ間でERR_CONNECTION_REFUSEDになる
- コンテナ間の通信でlocalhostを指定すると、そのコンテナ自身を指すため接続できません。docker-compose.ymlのサービス名(例: api)をホスト名として指定してください。
- Why does ERR_CONNECTION_REFUSED happen even though the server code looks correct?
- The browser resolved the hostname and sent a TCP SYN, but nothing was listening on that port when it arrived — the OS replied with RST. Common causes: the dev server crashed after starting, the port in the URL doesn't match the port the server actually bound to, or the server bound to a specific interface (or IPv6 ::1) that the request isn't reaching.
関連ツール / Related tools
- HTTP Status Code 検索 - 接続は成立しているが特定のステータスで失敗している場合の確認に