第13章:最短ルート:Caddyで“入口1個”を作る🚀🍞
この章は 「とにかく最速で成功体験」 がゴールだよ〜!🎉 やることはシンプルで、Caddy(入口)→ パスで2アプリに振り分け を作ります✨
http://localhost:8080/app1/→ アプリ①http://localhost:8080/api/→ アプリ②(APIっぽい)
0) 今日の完成図(超ざっくり)🗺️✨
ブラウザ
|
| http://localhost:8080/app1/ または /api/
v
[Caddy] ← 入口はここ1個だけ🚪
| \
| \---- /app1/* --> app1 (Node)
|
\------ /api/* --> api (Node)
1) まずは“動くダミーアプリ”を2つ作る🧪🍰
今回は Nodeの標準機能だけ でサクッとHTTPサーバを作るよ(依存なしで楽)😋 Node 24 は Active LTS 扱いで、いまの教材としても使いやすい立ち位置です。(Node.js)
フォルダ構成📁
chapter13-caddy/
compose.yaml
conf/
Caddyfile
app1/
server.js
api/
server.js
PowerShellでフォルダ作成🪟✨
mkdir chapter13-caddy
cd chapter13-caddy
mkdir conf, app1, api
app1/server.js(HTML返すだけ)🖥️
// app1/server.js
const http = require("http");
const port = process.env.PORT || 3000;
http
.createServer((req, res) => {
res.statusCode = 200;
res.setHeader("Content-Type", "text/html; charset=utf-8");
res.end(`
<h1>🐣 app1</h1>
<p>path: <b>${req.url}</b></p>
<p>👋 Caddy経由で来れてたら勝ち!</p>
`);
})
.listen(port, () => console.log(`app1 listening on ${port}`));
api/server.js(JSON返すだけ)🧃
// api/server.js
const http = require("http");
const port = process.env.PORT || 4000;
http
.createServer((req, res) => {
res.statusCode = 200;
res.setHeader("Content-Type", "application/json; charset=utf-8");
res.end(
JSON.stringify(
{
service: "api",
path: req.url,
message: "🍣 hello from api",
},
null,
2
)
);
})
.listen(port, () => console.log(`api listening on ${port}`));
2) Caddyfileを書いて「パスで振り分け」する🔀🍞
ここが本章の主役!✨
Caddyの handle_path は マッチしたプレフィックスを自動で取り除いて 中のサービスへ流してくれる便利機能だよ(例:/app1/hello → アプリ側には /hello として届く)🧠✨ (Caddy Web Server)
conf/Caddyfile 🧾
:80 {
# ✅ /app1/* は app1 に流す(/app1 を自動で剥がす)
handle_path /app1/* {
reverse_proxy app1:3000
}
# ✅ /api/* は api に流す(/api を自動で剥がす)
handle_path /api/* {
reverse_proxy api:4000
}
# ✅ それ以外は案内を返す(迷子対策)
handle {
respond "Try: /app1/ or /api/" 200
}
}
reverse_proxyは “指定先へ全部流す” の基本機能で、/api/*みたいにパス条件付きの例も公式に載ってるよ〜🧠 (Caddy Web Server)
3) compose.yaml(Caddy+2アプリを一気に起動)🐳🧩
Compose の推奨ファイル名は compose.yaml(または compose.yml)で、両方あれば compose.yaml を優先する仕様です。(Docker ドキュメント)
Caddy公式Dockerイメージは /data と /config をボリュームで持てる(特に /data は重要)という前提があるので、ここも素直に踏みます🧠🔒 (Docker Hub)
さらに、設定は /etc/caddy にフォルダごとマウントするのが推奨されてる流れ(直マウントの注意もある)なので、その形にします👍 (Docker Hub)
## compose.yaml
services:
caddy:
image: caddy:2
ports:
# まずは 8080 を入口にする(80が他と競合しやすいので)
- "8080:80"
volumes:
- ./conf:/etc/caddy
- caddy_data:/data
- caddy_config:/config
depends_on:
- app1
- api
app1:
image: node:24-alpine
working_dir: /app
volumes:
- ./app1:/app
command: ["node", "server.js"]
environment:
- PORT=3000
api:
image: node:24-alpine
working_dir: /app
volumes:
- ./api:/app
command: ["node", "server.js"]
environment:
- PORT=4000
volumes:
caddy_data:
caddy_config:
Caddy の安定版 Latest は v2.10.2(本日時点)なので、固定したい人は
image: caddy:2.10.2みたいにしてもOK👌 (GitHub)
4) 起動して動作チェック✅🎉
起動🚀
docker compose up -d
ブラウザでアクセス🌐
http://localhost:8080/app1/→ 🐣 app1 のHTMLhttp://localhost:8080/api/→ 🍣 api のJSONhttp://localhost:8080/→ “Try: /app1/ or /api/”
5) “Caddyfile変更→即反映”を体験する🔁✨
Caddyは「再起動なしで設定をリロードできる」流れがあるので、教材的にここで一回気持ちよくなっとこ😆
Dockerでのリロードは caddy reload をコンテナ内で叩くのが推奨ルートだよ。(Docker Hub)
例:Caddyfileの respond 文言を変える✍️
Try: ... の文章をちょい変えて保存 → 次を実行:
docker compose exec -w /etc/caddy caddy caddy reload
6) よくある詰まりポイント(最短で助かるやつ)🧯😇
① /app1/ が 404 になる😵💫
- Caddyfileのパスが
/app1/*になってる?(/app1だけだと微妙にハマることある) - ブラウザは
/app1/(末尾スラッシュあり)で試すのが無難✨
② 502(Bad Gateway)になる💥
だいたいこれ👇
- app1/api が落ちてる
- ポート番号がズレてる(app1:3000 / api:4000)
- サービス名を間違えてる(Caddyfileの
app1/apiが compose と一致してない)
確認コマンド🔍
docker compose ps
docker compose logs -f app1
docker compose logs -f api
docker compose logs -f caddy
③ “入口ポート”が開けない(起動はするけどアクセス不可)🚧
- 8080 が別アプリと競合してるときは、
compose.yamlの8080:80を別番号に変更(例:18080:80)で回避できるよ🛠️
7) ミニ課題(達成感強め)🎮🏁
課題A:3つ目のアプリ admin を増やして /admin/ に割り当てよう👑
admin/server.jsを作る(app1のコピペでOK)- compose に
adminサービス追加(PORT=5000とか) - Caddyfile に
handle_path /admin/* { reverse_proxy admin:5000 }追加
課題B:/api/hello にアクセスしたら JSON の path がどうなるか観察👀
handle_path が プレフィックスを剥がす挙動、体で覚えると一気に強くなる💪 (Caddy Web Server)
8) AIに投げる“勝ちテンプレ”🤖✨(コピペOK)
追加ルートを作らせる🧠
Docker Compose(compose.yaml)とCaddyfileがあります。
/admin/* を新しい admin サービスに reverse proxy したいです。
- admin は node:24-alpine で server.js を起動
- 外部にポート公開はしない
必要な compose.yaml の追記と Caddyfile の追記を最小差分で出して
502の原因を絞り込ませる🧯
Caddy経由で /api/ にアクセスすると 502 になります。
以下を前提に、原因候補を優先度順に3つに絞って、確認コマンドもセットでください:
- compose.yaml は caddy + app1 + api
- Caddyfile は handle_path /api/* { reverse_proxy api:4000 }
- api は node server.js
この章のまとめ🎁✨
- 入口1個(Caddy) を立てた!🚪
- パスで2アプリ共存 できた!🎉
handle_pathの「プレフィックス剥がし」が超重要だと分かった!🧠 (Caddy Web Server)- CaddyのDocker運用は
/dataをちゃんと永続化が基本だと押さえた!🔒 (Docker Hub)
次の章(第14章)に向けての一言だけ言うと… ここまで来たら、もう Caddyfileが怖くなくなるゾーン 入ってます😺✨