WebサイトやAPIを公開するとき、ほぼ確実に候補に上がるのが Nginx(エンジンエックス) です。
「とりあえず動く」だけなら簡単ですが、いざ本番運用に近づくと、
- どこに何を書くの?
locationの優先順位って?- 静的配信とアプリの中継(リバースプロキシ)って何が違うの?
- なんでNginxが速いって言われるの?
- どこを触ると落ちる(危ない)?
…みたいな壁にぶつかりがちです。
この記事では、Nginxの位置づけを「理解の言葉」で説明しつつ、手を動かすハンズオンで納得できるように構成しています。Dockerで再現性も高いので、ローカルで気軽に試せます。
Nginxとは?何を担当するソフト?
Nginxは一言でいうと 「Webの入口(エッジ)になれる万能サーバ」 です。主な用途は次のとおり。
1) Webサーバ(静的ファイル配信)
HTML/CSS/JavaScript/画像など、アプリ処理を介さずにそのまま返せるものを配信します。
静的配信は「速くて軽い」だけでなく、アプリの負荷を減らし、構成をシンプルにできます。
2) リバースプロキシ(バックエンド中継)
裏側で動くアプリ(Node.js / Python / Go / Java など)を直接インターネットに晒さず、Nginxを入口にして中継します。
- ブラウザ → Nginx → アプリ
の流れにすることで、アプリは「内側」で動くだけでOKになります。
3) ロードバランサ(振り分け)
アプリを複数台(複数コンテナ)に増やして、アクセスを分散します。
ここは入門の次のステップですが、Nginxがよく使われる理由のひとつです。
4) TLS終端(HTTPSの担当)
Let’s Encryptなどの証明書をNginxで扱い、アプリ側はHTTPのまま運用するパターンも多いです。
運用上は「証明書の更新」「暗号設定」「HTTP→HTTPSリダイレクト」などの責務を入口に寄せられます。
なぜNginxは速いと言われる?
「速い」の理由は一つではありませんが、入門として押さえると良いポイントは次の2つです。
- イベント駆動(イベントループ的な処理モデル)
たくさんの接続を効率よく扱いやすい。 - 静的配信が得意
ファイルを返すだけの処理が非常に強い。アプリを起動してテンプレを作るより圧倒的に軽い。
とはいえ、最初は「速い仕組み」を深掘りしすぎなくてOKです。
まずは Webの入口として整理されている ことと、設定が強力で実務に直結することを押さえるのが重要です。
Nginx設定の基本構造:入れ子を理解すれば読める
Nginxの設定ファイルは、ブロック構造を理解すればかなり読みやすくなります。
http { ... }:HTTP全体の設定(大枠)server { ... }:1サイト(1ホスト/1ポート)単位の設定location { ... }:URLパス単位のルール
イメージとしては、
serverは「このポートで待ち受け」「このホスト名のサイト」locationは「このパスに来たらどうする」
です。
よくある誤解は、「serverが複数あると何が起きるの?」です。
答えはシンプルで、listen と server_name の条件に応じて、どの server が応答するかが決まります。
よく使うディレクティブ(まずはこれだけ)
入門では「全部覚える」より、「使う順に覚える」方が失敗しません。
まずは以下のセットを押さえれば十分です。
静的配信に必要
listen 80;:待ち受けポートroot /path;:配信するファイルのディレクトリindex index.html;:デフォルトで返すファイルtry_files ...;:ファイルの存在チェック(ない場合の挙動)
リバースプロキシに必要
proxy_pass http://...;:転送先(裏のアプリ)proxy_set_header ...;:転送時のヘッダ(実務で重要)
ハンズオンの前提(Docker推奨)
ローカル環境を汚さず、確実に再現するためにDockerで進めます。
- Windows / macOS / Linux どれでもOK
- Docker Desktop(またはDocker Engine)が入っていればOK
- コマンドは
docker composeを使います
ハンズオン1:Nginxで「静的サイト」を配信してみる(最小構成)
まずは「Webサーバとして使う」を最短で体験します。
この段階で理解したいのは、Nginx=設定とファイルがあれば即配信できる という感覚です。
1. ディレクトリ構成
nginx-static/
├─ index.html
└─ docker-compose.yml
2. index.html を作成
index.html
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8" />
<title>Nginx 入門</title>
</head>
<body>
<h1>Nginxで静的サイト配信できた!</h1>
<p>Docker + Nginx の最小構成です。</p>
</body>
</html>
3. docker-compose.yml を作成
docker-compose.yml
services:
nginx:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./index.html:/usr/share/nginx/html/index.html:ro
ポイント:
nginx:alpineは軽量で手早いportsでホスト8080→コンテナ80に接続volumesでindex.htmlを配信ディレクトリにマウント:roで読み取り専用(安全)
4. 起動して確認
docker compose up -d
docker compose ps
ブラウザでhttp://localhost:8080
を開いてページが表示されれば成功です。
5. 何が起きているか(短く整理)
- Nginxはコンテナ内の
/usr/share/nginx/htmlを「静的配信のルート」として使う - そこに
index.htmlがあるので返せる - つまり「設定が最小でも動く」
ハンズオン2:設定ファイルを自作して server / location を理解する
次は「設定を変えたら挙動が変わる」体験をします。location を使えるようになると、Nginxの理解が一気に進みます。
1. 構成
nginx-conf/
├─ html/
│ ├─ index.html
│ └─ health.html
├─ nginx/
│ └─ default.conf
└─ docker-compose.yml
2. HTMLを作成
html/index.html
<h1>Top Page</h1>
<p>/ で表示されます</p>
html/health.html
OK
3. Nginx設定を作成
nginx/default.conf
server {
listen 80;
server_name _;
root /usr/share/nginx/html;
index index.html;
# /health は health.html を返す(完全一致)
location = /health {
default_type text/plain;
try_files /health.html =404;
}
# それ以外は静的配信(無ければ404)
location / {
try_files $uri $uri/ =404;
}
}
ここで理解したいのは2つです。
location = /health の意味
= は「完全一致」。/health にだけ反応し、/health/ や /healthcheck には反応しません。
try_files の意味
指定した順で「存在確認」して、見つかったらそれを返します。
見つからなければ最後の =404 が採用されます。
4. docker-compose.yml を作成
docker-compose.yml
services:
nginx:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./html:/usr/share/nginx/html:ro
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
5. 起動して確認
docker compose up -d
http://localhost:8080/→ Top Pagehttp://localhost:8080/health→ OK
6. 設定テスト(本番級に大事)
設定を変えたら必ずテストします。
docker compose exec nginx nginx -t
syntax is ok / test is successful が出ればOKです。
これを習慣化すると事故が激減します。
ハンズオン3:リバースプロキシ(Nginx → Node.js)を作る
ここが「実務で一番使う形」です。
アプリを直接外に出さず、Nginxを入口にして裏へ流します。
1. 構成
nginx-reverse-proxy/
├─ app/
│ ├─ server.js
│ └─ package.json
├─ nginx/
│ └─ default.conf
└─ docker-compose.yml
2. Node.jsアプリ
app/package.json
{
"name": "app",
"version": "1.0.0",
"type": "module",
"dependencies": {
"express": "^4.18.2"
}
}
app/server.js
import express from "express";
const app = express();
app.get("/", (req, res) => {
res.send("Hello from Node.js behind Nginx!");
});
app.get("/api", (req, res) => {
res.json({ message: "API OK", time: new Date().toISOString() });
});
app.listen(3000, () => console.log("app listening on 3000"));
3. Nginx設定(proxy_pass)
nginx/default.conf
upstream backend {
server app:3000;
}
server {
listen 80;
server_name _;
location / {
proxy_pass http://backend;
# 実務でよく入れるヘッダ
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
upstream を作っておくと、後で「複数台に増やす」拡張がしやすいです。
4. docker-compose.yml
docker-compose.yml
services:
nginx:
image: nginx:alpine
ports:
- "8080:80"
volumes:
- ./nginx/default.conf:/etc/nginx/conf.d/default.conf:ro
depends_on:
- app
app:
image: node:20-alpine
working_dir: /app
volumes:
- ./app:/app
command: sh -c "npm install && node server.js"
5. 起動 & 確認
docker compose up -d
docker compose logs -f app
http://localhost:8080/→Hello from Node.js behind Nginx!http://localhost:8080/api→ JSONが返る
ここで体感してほしいのは、クライアント(ブラウザ)からはNginxしか見えていないのに、裏のアプリが動いていることです。
つまり、アプリ側を入れ替えたり増やしたりしても、入口のURLは変えずに運用できます。
よくある落とし穴(入門でつまずきやすい)
1) localhost の罠
Dockerの世界では localhost は「そのコンテナ自身」です。
つまりNginxコンテナから見た localhost:3000 は「Nginxコンテナの3000番」で、Nodeコンテナには届きません。
コンテナ間通信は サービス名 を使います。
今回なら app:3000 が正解です。
2) 設定反映されない
設定ファイルを編集したのに挙動が変わらない場合は、まず確認:
docker compose exec nginx nginx -T | head -n 80
nginx -T は読み込んだ全設定を出力します。
「思っているファイルが読み込まれているか」を確認できるので強いです。
3) 404になる
root と try_files の組み合わせミスが多いです。
マウントしているディレクトリが合っているかを確認:
docker compose exec nginx ls -la /usr/share/nginx/html
ここまでできたら、Nginxはもう「入口」として使える
入門として重要なのは、Nginxを「単なるWebサーバ」ではなく、
- 静的配信ができる
locationでパスごとに処理を分けられるproxy_passで裏のアプリへ中継できる
という「入口の設計」に使えることです。
コメントを残す