localhost・0.0.0.0・127.0.0.1の違い|ポートとUnixソケットを理解して「つながらない」を解決
目次
- localhost・127.0.0.1・0.0.0.0の違い
- 127.0.0.1はループバック、localhostはその名前
- 0.0.0.0は「宛先」ではなく「待ち受けの範囲」
- Dockerコンテナで「外から見えない」問題
- コンテナのlocalhostはホストのlocalhostではない
- コンテナからホストにつなぐ:host.docker.internal
- ポート競合の確認とプロセスの特定
- ssとlsofで「誰が使っているか」を突き止める
- Unixドメインソケットの利点と権限
- 同一マシン内ならTCPよりソケットファイル
- ウェルノウンポートと1024未満の制約
- トラブル事例:ホストからは見えるのにコンテナから拒否される
- まとめ
「手元では動くのにDockerコンテナに入れた途端ブラウザから見えない」「コンテナの中からホストのDBに127.0.0.1でつなごうとして拒否される」「ポート3000が使用中と言われるが、何が使っているのかわからない」。こうしたトラブルの多くは、アプリのバグではなく「どのアドレスで待ち受けているか」の理解不足から起きています。
この記事では、localhost・127.0.0.1・0.0.0.0の意味の違いを起点に、バインドアドレスという概念、Dockerで外から見えない理由とhost.docker.internal、ポート競合の調べ方、Unixドメインソケットの利点と権限、1024未満のポートの制約までを解説します。読み終える頃には「つながらない」を仕組みから切り分けられるようになります。ipやssなどLinuxネットワークコマンドの基本は既知として進めます。
localhost・127.0.0.1・0.0.0.0の違い
127.0.0.1はループバック、localhostはその名前
127.0.0.1は「自分自身」を指すループバックアドレスで、この宛先に送ったパケットはネットワークカードを経由せずカーネル内で自分に戻ってきます。localhostはその名前で、/etc/hostsで127.0.0.1(IPv6では::1)に対応づけられています。ここで見落とされがちなのが、localhostという名前が127.0.0.1と::1のどちらに解決されるかは環境次第という点です。IPv4でしか待ち受けていないサーバーにlocalhostで接続すると、先にIPv6の::1を試して拒否される、といった問題がここから生まれます。
0.0.0.0は「宛先」ではなく「待ち受けの範囲」
サーバープログラムがポートを開くとき、「どのIPアドレス宛の通信を受け付けるか」を指定します。これがバインドアドレスです。
- 127.0.0.1にバインド:同じマシン内からの接続だけ受け付ける
- 0.0.0.0にバインド:そのマシンが持つすべてのIPv4アドレス宛の接続を受け付ける(IPv6では::)
- 特定のIP(例:192.168.1.10)にバインド:そのアドレス宛だけ受け付ける
0.0.0.0はクライアントが接続先として指定するものではなく、サーバーが「どこからでも受ける」と宣言するための特別な値です。ブラウザに http://0.0.0.0:3000 と打つとOSによってはlocalhostとして扱われますが、本来の使い方ではありません。
| 表記 | 意味 | 使う場面 |
|---|---|---|
| 127.0.0.1 | IPv4のループバック | 同一マシン内の接続先・内部専用サービスのバインド |
| ::1 | IPv6のループバック | 同上(IPv6) |
| localhost | 上記に解決される名前 | 設定ファイルや接続文字列 |
| 0.0.0.0 | 全IPv4インターフェースで待ち受け | コンテナ内・外部公開サービスのバインド |
Dockerコンテナで「外から見えない」問題
コンテナのlocalhostはホストのlocalhostではない
コンテナは独自のネットワーク名前空間を持つため、コンテナ内の127.0.0.1はコンテナ自身を指し、ホストとは別物です。docker runで -p 3000:3000 と指定すると、ホストの3000番宛の通信はコンテナの仮想NICのIPアドレス(例:172.17.0.2)の3000番に転送されます。ここでアプリが127.0.0.1にバインドしていると、172.17.0.2宛の接続はアプリの待ち受け範囲外なので拒否されます。これが「ポートを公開したのにつながらない」典型原因です。
多くの開発サーバーはデフォルトで127.0.0.1にバインドします。たとえばViteやFlaskの開発サーバー、Railsのbin/railsサーバー(古いバージョン)などで、コンテナ内では明示的に0.0.0.0を指定する必要があります。
# Vite(package.jsonのscriptsか、vite.config.tsのserver.host)
vite --host 0.0.0.0
# Flask
flask run --host=0.0.0.0 --port=5000
# Node.js(http.createServer)
server.listen(3000, '0.0.0.0');
# Gunicorn
gunicorn -b 0.0.0.0:8000 app:app
コンテナからホストにつなぐ:host.docker.internal
逆方向、つまりコンテナ内のアプリからホスト上で動いているDBやAPIに接続したい場合も、127.0.0.1ではコンテナ自身を指してしまいます。Docker DesktopではDNS名 host.docker.internal がホストを指すよう用意されています。LinuxのDocker Engineでは自動で解決されないため、起動時に --add-host で明示します。
# Linuxでhost.docker.internalを使えるようにする
docker run --add-host=host.docker.internal:host-gateway -p 3000:3000 myapp
# docker-compose.yml
services:
app:
image: myapp
extra_hosts:
- "host.docker.internal:host-gateway"
environment:
DATABASE_URL: postgres://user:pass@host.docker.internal:5432/mydb
ただし、ホスト側のPostgreSQLが127.0.0.1にしかバインドしていなければ、この接続も拒否されます。ホスト側のlisten_addressesにDockerブリッジのアドレス(172.17.0.1など)を含める必要があり、これは同時にファイアウォールでの制限も検討すべき変更です。コンテナ同士の接続であれば、同じComposeネットワーク内でサービス名をホスト名として使うのが本筋で、詳しくはDocker Compose実践ガイドを参照してください。
ポート競合の確認とプロセスの特定
ssとlsofで「誰が使っているか」を突き止める
「Address already in use」や「EADDRINUSE」が出たら、そのポートをどのプロセスが開いているかを確認します。ssは待ち受けソケットの一覧を、lsofはファイルディスクリプタの観点からプロセスを特定します。
# TCPで待ち受け中のソケットとプロセスを一覧(要root/sudoでプロセス名表示)
sudo ss -tlnp
# 特定ポートだけ
sudo ss -tlnp 'sport = :3000'
# lsofでポート3000を開いているプロセス
sudo lsof -i :3000
# macOSの場合
lsof -nP -iTCP:3000 -sTCP:LISTEN
出力の Local Address 列が 127.0.0.1:3000 か 0.0.0.0:3000 か *:3000 かを見れば、そのサービスが内部専用なのか外部にも開いているのかが一目でわかります。意図せず0.0.0.0で開いているDBや管理画面がないか、本番サーバーではこの一覧を定期的に確認する習慣をつけてください。ファイアウォールで塞いでいるから大丈夫、と考えるより、そもそも内部専用のものは127.0.0.1にバインドする方が確実です。
Unixドメインソケットの利点と権限
同一マシン内ならTCPよりソケットファイル
Unixドメインソケットは、TCP/IPを使わずファイルシステム上のパス(例:/run/php/php8.3-fpm.sock)を通じてプロセス間通信を行う仕組みです。ネットワークスタックを通らないためオーバーヘッドが小さく、ポート番号の競合も起きません。NginxとPHP-FPM、NginxとGunicorn、アプリとPostgreSQLなど、同じマシン内で完結する接続に向いています。
最大の利点はアクセス制御をファイルのパーミッションで行えることです。TCPの127.0.0.1バインドは「そのマシン上の全ユーザー」が接続できてしまいますが、ソケットファイルなら所有者とグループで接続できるユーザーを絞れます。
# Gunicornをソケットで起動(所有者・グループ・パーミッション指定)
gunicorn -b unix:/run/myapp/app.sock -m 660 --user myapp --group www-data app:app
# Nginx側
upstream app {
server unix:/run/myapp/app.sock;
}
# PostgreSQLにソケット経由で接続(ホスト名にディレクトリを指定)
psql -h /var/run/postgresql -U myuser mydb
Nginxのワーカーが動くユーザー(www-dataなど)がソケットファイルのグループに含まれていないと、接続時に「Permission denied」が出ます。ソケット経由の連携の全体像はリバースプロキシの設定もあわせて確認してください。
ウェルノウンポートと1024未満の制約
0〜1023番はウェルノウンポートと呼ばれ、HTTP(80)やHTTPS(443)、SSH(22)などが割り当てられています。Linuxではこれらのポートをroot権限なしで開くことはできません。開発サーバーを80番で起動して「Permission denied」になるのはこのためです。対処は、アプリは8080など高いポートで一般ユーザーとして動かし、前段のNginxが80/443を受けて転送するのが標準的です。どうしてもアプリが直接開く必要があれば、setcapで特定バイナリにだけ権限を与える方法があります。
# Node.jsのバイナリに1024未満のポートを開く権限だけを付与
sudo setcap 'cap_net_bind_service=+ep' $(readlink -f $(which node))
rootでアプリを起動して済ませるのは、アプリの脆弱性がそのままroot権限の奪取につながるため避けてください。
トラブル事例:ホストからは見えるのにコンテナから拒否される
症状:ホストで動くPostgreSQLに、psql -h localhost では接続できるのに、コンテナからhost.docker.internal:5432で接続すると「Connection refused」になる。
原因:PostgreSQLのデフォルトlisten_addressesは 'localhost' で、127.0.0.1と::1にしかバインドしていない。host.docker.internalはホストのブリッジ側アドレス(172.17.0.1など)に解決されるため、待ち受け範囲外だった。ssで確認すると Local Address が 127.0.0.1:5432 のみだった。
対処:postgresql.confのlisten_addressesに 'localhost,172.17.0.1' を追加し、pg_hba.confでコンテナのサブネット(172.17.0.0/16)からの接続を許可して再起動。あわせてファイアウォールで5432番をDockerブリッジ以外から遮断した。最終的にはDBもコンテナ化してComposeネットワーク内でサービス名で接続する構成に変更し、この問題自体をなくした。
まとめ
127.0.0.1は自分自身への宛先、localhostはその名前(IPv4とIPv6のどちらかは環境次第)、0.0.0.0はサーバーが「全インターフェースで待ち受ける」と宣言するためのバインドアドレスです。コンテナ内では0.0.0.0にバインドしないと外から見えず、コンテナからホストへはhost.docker.internalを使い、ホスト側も待ち受け範囲を広げる必要があります。競合や意図しない公開はss -tlnpで確認し、同一マシン内の接続はUnixソケットでパーミッション制御を効かせ、1024未満のポートはNginxに任せる、というのが実務の基本判断です。開発環境と本番環境のネットワーク構成を整理したい場合は、Harmonic Societyのシステム開発・インフラ支援にご相談ください。
Harmonic Society
この記事の内容、自社の業務でも活かせそうですか?
ローカルLLM・AI・クラウドなどの技術導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK。まずは30分の無料相談から。売り込みはしません。
関連記事
Related / 9 articles
Notes & Insights
- プログラミング
DDoS攻撃の仕組みと対策入門|レイヤー別の防御とCDN・クラウドの活用
DDoS攻撃をボリューム型・プロトコル型・アプリ層に分けて仕組みを解説し、自前サーバーで防げない理由、CloudflareやAWS Shieldの標準防御、オリジンIPの隠し方、レートリミットとBot対策、攻撃を受けたときの初動、費用が跳ね上がるDenial of Walletへの備えまでわかります。
- プログラミング
WAFとは?仕組み・導入パターン・誤検知対策|Webアプリを攻撃から守る実践ガイド
WAFがファイアウォールやIDSと何が違うのか、シグネチャとマネージドルールの仕組み、Cloudflare WAF・AWS WAF・ModSecurityの比較、フォーム送信がブロックされる誤検知の調査と例外設定、ログ監視、WAFが代替できないことまで実践的に解説します。
- プログラミング
セキュリティヘッダー入門|CSP・HSTS・X-Frame-Optionsの設定と効果を実践解説
CSP・HSTS・X-Frame-Options・X-Content-Type-Optionsなど主要セキュリティヘッダーが防ぐ攻撃と、CSPのReport-Onlyからの段階導入、nonce/hash、HSTS preloadの不可逆リスク、Nginx・Next.js・Astroでの設定例、確認方法を解説します。
- プログラミング
クラウドの通信費(Egress)入門|データ転送量課金の仕組みと転送コストを抑える設計
クラウドの「受信無料・送信有料」の原則、AZ間・リージョン間・インターネット向けの単価差、NATゲートウェイ処理料の罠、CDNで転送量を減らす方法、バックアップやログ転送の見落とし、請求書で転送料を特定する手順を解説。想定外の請求を防げます。
- プログラミング
秘密情報をGitに入れない仕組み|.gitignore・git-secrets・履歴から漏れた鍵の削除
APIキーや.envをGitにコミットしてしまう典型経路と、.gitignore・.env.exampleの運用、pre-commitでのgitleaks検知、GitHub secret scanningの活用、漏れた鍵の無効化と履歴書き換え(git filter-repo)の手順を解説。仕組みで再発を防げます。
- プログラミング
開発・ステージング・本番環境の分離設計|環境差分をなくす構成とアクセス制御
開発・ステージング・本番それぞれの目的と、構成をコードで揃える方法、環境別の設定注入、本番データを使わないテストデータ戦略、ステージングの保護(Basic認証・IP制限・noindex)、コストを抑える運用までを解説。環境差分による本番障害を防げます。
- プログラミング
ngrok・Cloudflare Tunnelでローカルを公開|Webhook開発とデモ環境の作り方
NAT内のローカル環境にStripeやLINEのWebhookを届けるトンネリングの仕組みを解説。ngrok・Cloudflare Tunnel・localtunnelの比較、固定ドメインと認証、リクエスト検査、公開時のセキュリティ、自宅サーバー公開への応用までわかります。
- プログラミング
ローカル開発環境のHTTPS化|mkcert・hostsファイル・自己署名証明書の正しい使い方
ローカル開発をHTTPS前提にすべき理由(Secure Cookie・Service Worker・OAuth)と、mkcertでローカルCAを作りhostsで独自ドメインを割り当ててVite・Next.js・Dockerで使う手順を解説。証明書警告を無視する癖の危険も理解できます。
- プログラミング
GitOps入門|ArgoCD・FluxでGitをインフラの正とするデプロイ運用
GitOpsのPush型とPull型の違い、差分検知と自動同期の仕組み、リポジトリ構成、ArgoCDの最小導入手順までを解説。git revertで戻せるデプロイ運用が理解でき、Kubernetes以外への応用の考え方もわかります。
Harmonic Society
「読んで終わり」にせず、自社の業務で試してみませんか?
AI・ローカルLLM・クラウドの導入を、要件整理からPoC・社内展開まで代表エンジニアが伴走します。オンライン対応・全国OK・売り込みなし。
無料・30分・オンラインOK|1営業日以内に返信します