---
layout: post
title: "VM のサーバーに接続できない？localhost・127.0.0.1・0.0.0.0 の違い"
description: "VM 内のサーバーが起動しているのにホストから接続できない原因を、localhost、127.0.0.1、0.0.0.0、NAT、Vagrant、Docker の仕組みから解説します。"
author: Mark_Mew
tags: [Docker, network]
keywords: [Docker, network, localhost, 127.0.0.1, 0.0.0.0]
lang: ja
date: 2026-06-27
---

以前から私は、自分の PC 上に VM を作り、その中で開発するようにしています。主な目的は、開発環境とリソース構成をそろえることです。それぞれの VM に同程度の CPU とメモリを割り当てておけば、開発者ごとの PC 性能の違いによる影響を抑えられます。

しかし、そこで何度も同じ問題に遭遇しました。VM 内ではサーバーが正常に起動しているのに、ホスト側のブラウザからどうしても接続できないのです。

その後、ようやく重要な違いを理解しました。**ホストの localhost は VM の localhost ではなく、ホストにとっての「自分自身」は VM にとっての「自分自身」でもありません。**

この問題を Google で調べると、多くの場合は次の答えにたどり着きます。

> `127.0.0.1` を `0.0.0.0` に変更すれば接続できる。

確かにこの方法で解決することは多いのですが、なぜアドレスを 1 つ変更するだけで接続できるのでしょうか。この記事では基本から順に、`localhost`、`127.0.0.1`、`0.0.0.0` の違いと、VM や Docker の中でそれぞれがどのような役割を持つのかを説明します。

## 先に結論

| 名前 | 種類 | 意味 | 主な用途 |
| --- | --- | --- | --- |
| `localhost` | Hostname | ローカルマシンのホスト名 | 人が扱いやすい名前でローカルマシンを表す |
| `127.0.0.1` | IP Address | IPv4 のループバックアドレス | 同じネットワーク環境内からのアクセスだけを受け付ける |
| `0.0.0.0` | Special Address | 利用可能なすべての IPv4 ネットワークインターフェース | 複数のインターフェースからの接続を受け付ける |

最も重要なポイントは次のとおりです。

> `127.0.0.1` が表すのは、現在のネットワーク環境における「自分自身」だけであり、ホストではありません。

## localhost：IP アドレスではなく名前

`localhost` は IP アドレスではなく、Hostname（ホスト名）です。多くのシステムでは、`hosts` ファイルに次のような対応関係が定義されています。

```text
127.0.0.1 localhost
```

そのため、`localhost` は通常 `127.0.0.1` に名前解決されます。システムによっては、IPv6 の `::1` が優先される場合もあります。

簡単にまとめると、次のようになります。

- `localhost` は名前。
- `127.0.0.1` は実際に使用される IPv4 のループバックアドレス。

実際の利用では置き換えられることが多いものの、概念としては同じではありません。

## 127.0.0.1：自分自身からの接続だけを受け付ける

`127.0.0.1` は IPv4 の Loopback Address（ループバックアドレス）です。このアドレス宛てのパケットは物理ネットワークインターフェースを通らず、現在のネットワーク環境へそのまま戻されます。

たとえば、アプリケーションを次のように設定したとします。

```python
app.run(host="127.0.0.1", port=5000)
```

この設定では、サーバーはループバックインターフェースだけを Listen します。つまり、同じマシン、Container、または VM の内部からの接続しか受け付けません。

この PC の LAN 内の IP アドレスが `192.168.1.100` だとすると、ローカルでは次の URL に接続できます。

```text
http://127.0.0.1:5000
http://localhost:5000
```

しかし、ほかのデバイスから次の URL には接続できません。

```text
http://192.168.1.100:5000
```

サーバーが `192.168.1.100` のネットワークインターフェースを Listen していないためです。

## 0.0.0.0：すべてのネットワークインターフェースを Listen する

サーバーの Bind 設定における `0.0.0.0` は、利用可能なすべての IPv4 ネットワークインターフェースを Listen するという意味です。実際に接続先として使用する特定の IP アドレスではありません。

1 台の PC が、同時に次のような複数のアドレスを持つことがあります。

```text
127.0.0.1
192.168.1.100
10.0.0.5
```

アプリケーションを次のように設定するとします。

```python
app.run(host="0.0.0.0", port=5000)
```

サーバーは利用可能なすべてのインターフェースで `5000` Port を Listen するため、次の各アドレスから接続できる可能性があります。

```text
127.0.0.1:5000
192.168.1.100:5000
10.0.0.5:5000
```

ただし、`0.0.0.0` はサーバー側の**待ち受け設定**であり、通常はブラウザに入力する接続先ではありません。ブラウザでは、ホストや VM が実際に持っていて、接続元から到達できる IP アドレスを使用します。

> `0.0.0.0` に Bind すると、同じネットワーク上の別のデバイスからもサービスへ接続できる場合があります。開発時には便利ですが、意図せずサービスを公開しないよう、ファイアウォール、認証、適切なネットワーク制限も組み合わせる必要があります。
{: .prompt-warning}

## VM が 127.0.0.1 に Bind すると、なぜホストから接続できないのか？

ネットワークの観点では、VM は独立した別のコンピューターとして動作します。ホストと VM は、それぞれ独自のループバックインターフェース、IP アドレス、ネットワークインターフェースを持っています。

```text
Host
├── localhost / 127.0.0.1
└── 192.168.56.1

VM
├── localhost / 127.0.0.1
└── 192.168.56.101
```

VM 内のアプリケーションを次のように設定した場合を考えます。

```python
app.run(host="127.0.0.1", port=5000)
```

このアプリケーションは、VM 自身のループバックインターフェースだけを Listen します。

```text
lo    127.0.0.1       ← Listen している
eth0  192.168.56.101  ← Listen していない
```

そのため、VM 内で `http://127.0.0.1:5000` を開けば接続できますが、ホストから `http://192.168.56.101:5000` へアクセスすることはできません。

次のように変更すると、状況が変わります。

```python
app.run(host="0.0.0.0", port=5000)
```

サーバーは `lo`、`eth0` など、VM が持つ各インターフェースを Listen します。ホストから VM のネットワークへ到達できれば、次の URL で接続できます。

```text
http://192.168.56.101:5000
```

## 0.0.0.0 に変更しても接続できないのはなぜか？

「サービスが Listen しているか」と「ネットワークとして到達できるか」は、別の階層の問題だからです。

- **Bind**：サーバーは、そのネットワークインターフェースからの接続を受け付けているか。
- **Routing**：ホストから VM までパケットを届けられるか。
- **Firewall**：OS やクラウドのファイアウォールルールが、その Port の通信を許可しているか。

`0.0.0.0` で解決できるのは Bind の問題だけです。ホストから VM へ到達できるかどうかは、VM のネットワークモードやファイアウォール設定にも左右されます。

### NAT

NAT は、よく使用されるデフォルトのネットワークモードです。

```text
Host → Virtual NAT → VM → Internet
```

通常、VM から Internet へは接続できますが、外部から VM へ直接接続を開始することはできません。ホストから VM 内のサービスへ接続するには、ホストの `5000` Port を VM の `5000` Port へ転送するような **Port Forwarding** の設定が必要です。

> Vagrant の `forwarded_port` は、まさにこの構成に当たります。次の設定では、ホストの `8080` Port を VM の `5000` Port へ転送します。また、`host_ip` を指定することで、ホスト自身からだけ接続できるように制限しています。
>
> ```ruby
> config.vm.network "forwarded_port",
>   guest: 5000,
>   host: 8080,
>   host_ip: "127.0.0.1"
> ```
>
> このときの接続経路は、`Host の 127.0.0.1:8080 → Vagrant NAT Port Forwarding → VM の 5000 Port → Server` です。そのため、VM 内のサーバーは引き続き `0.0.0.0:5000` を Listen する必要があります。VM 自身の `127.0.0.1:5000` だけを Listen している場合、VM のネットワークインターフェースへ転送された通信はサーバーまで届きません。設定後に `vagrant reload` を実行すれば、ホストから `http://127.0.0.1:8080` でサービスへ接続できます。
{: .prompt-info}

### Host-Only

Host-Only は、ホストと VM の通信だけに使う仮想ネットワークを作成します。

```text
Host  192.168.56.1
  ↕
VM    192.168.56.101
```

サービスが VM の対応するインターフェースを Listen していれば、ホストから次の URL で接続できます。

```text
http://192.168.56.101:5000
```

### Bridged

Bridged モードでは、VM がホストと同じ物理ネットワークへ直接参加します。

```text
Router
├── Host  192.168.1.100
└── VM    192.168.1.101
```

この場合、VM は LAN 上のもう 1 台のコンピューターとほぼ同じです。ネットワークとファイアウォールが許可していれば、ホストやスマートフォンから次の URL へ接続できます。

```text
http://192.168.1.101:5000
```

## VM のどの IP アドレスへ接続すればよいのか？

VM には、複数のネットワークインターフェースが同時に存在する場合があります。

```text
lo       127.0.0.1
ens33    10.0.2.15
ens34    192.168.56.101
docker0  172.17.0.1
```

サーバーが `0.0.0.0:5000` を Listen していても、ブラウザに入力する IP アドレスは、次の条件によって決まります。

- ホストからそのサブネットへ到達できるか。
- VM が NAT、Host-Only、Bridged のどのモードを使用しているか。
- VM とホストのファイアウォールが `5000` Port を許可しているか。

Linux VM では、次のコマンドで IP アドレスを確認できます。

```bash
ip addr
```

アプリケーションが実際に Listen しているアドレスと Port は、次のコマンドで確認できます。

```bash
ss -lntp
```

## Docker でも 0.0.0.0 に Bind する必要があるのはなぜか？

Container にも独立したネットワーク環境があります。Container 内のアプリケーションが、次のアドレスだけを Listen しているとします。

```python
app.run(host="127.0.0.1", port=5000)
```

Container の起動時に Port Mapping を設定していても、

```bash
docker run -p 5000:5000 my-app
```

ホストから接続できない場合があります。アプリケーションが Container 内部のループバックインターフェースからの接続しか受け付けていないためです。

アプリケーションを次のように変更します。

```python
app.run(host="0.0.0.0", port=5000)
```

これでサーバーは Container のネットワークインターフェースも Listen するようになり、Docker の Port Mapping がホストからの接続をアプリケーションへ転送できるようになります。

## 接続できないときの確認手順

接続に失敗した場合は、次の順番で確認できます。

1. **アプリケーションは本当に起動しているか？** VM または Container の内部で `curl http://127.0.0.1:5000` を実行して確認します。
2. **アプリケーションはどこを Listen しているか？** `ss -lntp` を使い、`127.0.0.1:5000` と `0.0.0.0:5000` のどちらを Listen しているか確認します。
3. **VM の IP アドレスは何か？** `ip addr` を使って、ホストから到達できる IP アドレスを探します。
4. **VM のネットワークモードは正しいか？** NAT では通常 Port Forwarding が必要です。Host-Only または Bridged では、対応する VM の IP アドレスを使用します。
5. **ファイアウォールが通信を許可しているか？** VM、ホスト、またはクラウド環境で対象の Port がブロックされていないか確認します。
6. **Docker を使用している場合、Port は公開されているか？** `docker ps` に想定どおりの Port Mapping が表示されているか確認します。

## まとめ

- `localhost` はホスト名であり、通常は `127.0.0.1` または `::1` に名前解決されます。
- `127.0.0.1` はループバックアドレスであり、現在のネットワーク環境における自分自身だけを表します。
- `0.0.0.0` は、サーバーにすべての IPv4 ネットワークインターフェースを Listen させるための設定であり、ブラウザが接続先として使用する実際の IP アドレスではありません。
- ホスト、VM、Container は、それぞれ独自のネットワーク環境と `localhost` を持っています。
- サービスが Listen していても、必ずネットワークから到達できるとは限りません。Routing、VM のネットワークモード、ファイアウォールも確認する必要があります。

これらの概念を理解すれば、VM や Docker だけでなく、Kubernetes、クラウド環境、物理サーバーでネットワークの問題が発生したときにも、どの階層に原因があるのかをすばやく判断できます。単に「`127.0.0.1` を `0.0.0.0` に変更する」とだけ覚える必要はなくなります。
