Webアプリケーションの運用や開発では、複数のサービスを安全に公開し、SSL/TLS証明書を適切に管理するために、リバースプロキシが役立ちます。NginxやApacheでも実現できますが、証明書の取得・更新を自動化するには、構成に応じて外部ツールなどとの連携が必要です。
そこで注目したいのが、Webサーバー兼リバースプロキシの「Caddy」です。Caddyは、シンプルで読みやすいCaddyfileと、自動HTTPS機能を備えています。Docker環境でも利用できるため、複数のWebアプリケーションを少ない設定で公開したい場合に適しています。
この記事では、Caddyの reverse_proxy ディレクティブの基本から、Docker Composeを用いたNode.jsアプリとの連携、パスやサブドメインごとのルーティング、よくあるエラーの切り分け方法まで、実際に手を動かして再現できる手順を分かりやすく解説します。
◆◇◆ 【衝撃価格】VPS512MBプラン!1時間1.3円【ConoHa】 ◆◇◆Caddy reverse proxyとは?仕組みとできること
リバースプロキシの基本とCaddyが担う役割
リバースプロキシ(Reverse Proxy)は、インターネットからのリクエストを最初に受け取り、バックエンドで稼働するWebアプリケーションやAPIサーバーへ適切に転送(プロキシ)するサーバーのことです。
Webサーバーは、静的なHTMLファイルや画像などをクライアントへ直接配信できます。一方、リバースプロキシとして構成すると、受け取ったリクエストをバックエンドのWebアプリケーションやAPIサーバーへ転送できます。ApacheやNginxも、設定によってこれら両方の役割を担うことが可能です。
[クライアント(ブラウザー)]
│
▼ (HTTPS / ポート 443)
[Caddy(リバースプロキシ)]
│
├─► (転送) ──► [バックエンドアプリ 1 (Node.js / Port 3000)]
│
└─► (転送) ──► [バックエンドアプリ 2 (API / Port 4000)]
リクエストが届いてからレスポンスが返るまでの流れは次のとおりです。
- リクエストの受信: クライアント(ブラウザーなど)からのリクエストを、Caddyが公開ポート(通常はHTTPの80番、HTTPSの443番)で受け付けます。
- ルーティング・処理: Caddyは設定ファイル(Caddyfile)のルールに基づき、どのバックエンドへ転送すべきかを判断します。この際、SSL/TLSの終端(暗号化の解除)や、クライアント情報のヘッダー付与なども同時に行われます。
- バックエンドへの転送: 内部ネットワークを経由して、バックエンドで待機しているアプリケーションへリクエストを送信します。
- レスポンスの返却: バックエンドから返されたレスポンスをCaddyが受け取り、必要に応じてヘッダー等を調整した上で、クライアントへ返却します。
Caddyはこのリバースプロキシとしての機能をシンプルかつ安全に実現できるように設計されており、特に面倒なSSL/TLS証明書の管理を自動化できる点が大きな特徴です。
Caddyでできること:自動HTTPS・ドメイン振り分け・APIプロキシ
Caddyをリバースプロキシとして利用すると、現代のWeb運用に欠かせない複雑な設定を、数行のシンプルな「Caddyfile」で実現できます。主な用途として、次のような機能があげられます。
1. Let’s Encrypt等を利用したHTTPSの自動化
Caddyの大きな特徴は、SSL/TLS証明書の取得・更新とHTTPSへのリダイレクトを自動化する機能が標準で組み込まれている点です。NginxなどでもCertbot等のACMEクライアントと連携して自動化できますが、環境に応じて追加の導入や設定が必要になります。
2. ホスト名(ドメイン・サブドメイン)に基づく振り分け
1台のサーバー上で複数のWebアプリケーションを運用する場合、アクセスされたドメインやサブドメインに応じて、異なるバックエンドコンテナへリクエストを自動でルーティングできます。
3. パスに応じたAPIプロキシ
同一ドメインの下で、フロントエンドの静的ファイル配信とバックエンドのAPIサーバーへの転送をスムーズに切り分けることができます。
以下は、これらの特徴を組み合わせた簡潔なCaddyfileの例です。
# 1. 独自ドメインでのHTTPS自動化とNode.jsアプリへの転送
example.com {
reverse_proxy localhost:3000
}
# 2. サブドメインごとの振り分け
api.example.com {
reverse_proxy localhost:4000
}
このように、Caddyは「HTTPS化」「マルチドメイン・サブドメインのルーティング」「バックエンドへの安全なプロキシ」を最小限の記述で完結させることができます。実際の運用では、Docker Compose環境と組み合わせることで、さらにメンテナンス性の高い構成を構築可能です。
Nginx・Apache・Traefikとの違いとCaddyを選ぶべきケース
リバースプロキシとして広く利用されているソフトウェアには、Nginx、Apache、Traefikなどがあります。それぞれの特徴を理解し、プロジェクトの要件に合ったものを選ぶことが重要です。
主要なリバースプロキシの比較
| 評価項目 | Caddy | Nginx | Apache | Traefik |
|---|---|---|---|---|
| 設定方法 | 読みやすいCaddyfileで設定できる | ディレクティブを組み合わせて柔軟に設定できる | VirtualHostや各種モジュールを利用して設定する | ルーターやミドルウェアなどの概念で設定する |
| HTTPS運用 | 自動HTTPSが標準機能として組み込まれている | CertbotなどのACMEクライアントとの連携で自動化できる | ACMEクライアントなどとの連携で自動化できる | ACMEを利用した証明書の自動取得・更新に対応する |
| Docker連携 | 公式イメージとComposeのネットワーク設定で連携できる | コンテナ内で動作し、内部ネットワーク経由で連携できる | コンテナ内で動作させ、内部ネットワーク経由で連携できる | Dockerラベルなどによるサービス検出と動的なルーティングに強い |
| ルーティング | ドメイン・パス・各種マッチャーで振り分けられる | 柔軟なロケーション設定やリライトルールを利用できる | VirtualHostやmod_rewriteなどで制御できる | ルーターやミドルウェアを組み合わせて制御できる |
| 適した用途 | 設定の簡潔さやHTTPS自動化を重視するWebサイト・API | 幅広いWebシステムや既存の運用基盤 | 既存のApache構成やモジュール資産を活用するシステム | Docker・Kubernetes環境でサービスの変化に追従する構成 |
Caddyが向いているケース
- HTTPS証明書の管理を自動化したい場合:証明書の取得・更新をCaddyに任せ、運用時の作業を減らしたい場合に適しています。
- 設定ファイルをシンプルに保ちたい場合:Caddyfileで、サイトアドレスと転送先を中心にリバースプロキシを設定できます。
- Docker Composeで複数のWebアプリを公開したい場合:各サービスを同じネットワークに接続し、サービス名を使って転送先を指定できます。
ほかの選択肢が向いているケース
- 既存のNginx設定や運用ノウハウを活用したい場合:現在の構成を維持することが、移行コストや運用負荷の面で合理的な場合があります。
- DockerやKubernetes上のサービスを自動検出したい場合:Dockerラベルなどを利用してルーティングを動的に管理したいなら、Traefikも有力な選択肢です。
- 既存のApacheモジュールやVirtualHost構成を活用したい場合:既存環境との互換性や移行のしやすさを優先すると、Apacheを継続利用するほうが適している場合があります。
Caddyの基本設定 Caddyfileとreverse_proxyディレクティブ

Caddyのインストール方法とCaddyfileの配置場所
CaddyをLinux環境(Ubuntu / Debianなど)に導入する場合、公式APTリポジトリを利用するのが最も安定的かつ推奨される方法です。バイナリを手動で配置する方法もありますが、パッケージ管理を利用することで、systemdを通じた自動起動やデーモン管理が容易になります。
Ubuntu / Debianへのインストール手順
公式リポジトリを追加し、APT経由でインストールを実行します。
# 1. 必要な依存パッケージのインストール
sudo apt install -y debian-keyring debian-archive-keyring apt-transport-https curl
# 2. 公式GPGキーの追加
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/gpg.key' | sudo gpg --dearmor -o /usr/share/keyrings/caddy-stable-archive-keyring.gpg
# 3. 公式APTリポジトリの登録
curl -1sLf 'https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt' | sudo tee /etc/apt/sources.list.d/caddy-stable.list
# 4. APTからGPGキーとリポジトリ情報を読み取れるようにする
sudo chmod o+r /usr/share/keyrings/caddy-stable-archive-keyring.gpg
sudo chmod o+r /etc/apt/sources.list.d/caddy-stable.list
# 5. パッケージリストの更新とCaddyのインストール
sudo apt update
sudo apt install caddy
インストールが完了したら、バージョン確認とサービスの起動状態を確認します。
# バージョン確認
caddy version
# systemdサービスの起動状態確認
sudo systemctl status caddyCaddyfileとデータの保存場所
Caddyの公式パッケージをsystemd環境にインストールした場合、設定ファイル(Caddyfile)の標準的な配置場所は /etc/caddy/Caddyfile です。公式のsystemdサービス設定では、Caddyの実行ユーザーのホームディレクトリが /var/lib/caddy となるため、証明書などのデータは通常 /var/lib/caddy/.local/share/caddy、自動保存されたJSON設定などは /var/lib/caddy/.config/caddy に保存されます。
これらの保存先は、実行ユーザーや環境変数、サービス設定によって変わる場合があります。実際の環境では、systemctl cat caddy などでサービス設定を確認してください。
Docker環境では、コンテナ内の /etc/caddy/Caddyfile に設定ファイルを配置します。ホスト側のディレクトリは任意に決められますが、マウント先と実際のファイル配置を一致させてください。
Caddyfileの基本構文とreverse_proxyディレクティブの書き方
Caddyfileは、Caddy専用のシンプルで人間が読みやすい設定ファイルです。波括弧 {} を用いたブロック構造を基本とし、複雑なNginxの設定ファイルを簡潔に置き換えることができます。
Caddyfileの基本構造
Caddyfileの基本的な書き方は、「サイトアドレス(サイトブロック)」の後に、適用したい「ディレクティブ(機能)」を記述します。
example.com {
reverse_proxy localhost:3000
}
この記述が持つ意味は次のとおりです。
example.com: リクエストを受け付けるサイトのアドレス(ホスト名)を指定します。reverse_proxy: 受信したリクエストを別のサーバーへ転送するディレクティブです。localhost:3000: 転送先のバックエンドサーバー(この場合は同一サーバー内のポート3000で稼働しているNode.jsアプリケーションなど)を指定しています。
CaddyfileのHTTPSバックエンド設定
複数のサイトや、HTTPSで動作するバックエンドへ転送する場合は次のように記述します。
# HTTPのバックエンドに転送する場合
app.example.com {
reverse_proxy web-app:3000
}
# プライベートCAが発行した証明書を使用するHTTPSバックエンド
secure-backend.example.com {
reverse_proxy <https://backend.internal.example:8443> {
transport http {
tls_trust_pool file /etc/caddy/certs/backend-ca.pem
}
}
}
1つ目の例では、HTTPで待ち受けるバックエンドへリクエストを転送します。2つ目の例では、HTTPSで動作するバックエンドの証明書を検証するため、バックエンド証明書の発行元となるCA証明書を指定しています。
backend.internal.example は、Caddyから名前解決でき、証明書のホスト名と一致するように構成してください。上記のCA証明書ファイルも、Caddyから読み取れる場所に配置する必要があります。
証明書の検証を無効にする tls_insecure_skip_verify は、通信相手の正当性を確認できなくなるため、安易に使用しないでください。
設定の検証・再読み込み方法:caddy validateとcaddy reloadの使い方
Caddyfileを編集した後は、記述ミスがないかを検証し、ダウンタイムなしで稼働中のプロセスに設定を反映させることができます。
1. 設定ファイルの検証 (caddy validate)
Caddyfileを変更したら、稼働中のCaddyに反映する前に設定を検証します。
# 標準の配置場所(/etc/caddy/Caddyfile)を検証する場合
sudo caddy validate --config /etc/caddy/Caddyfile
設定が正しく読み込める場合は、検証成功を示すメッセージが出力されます。エラーがある場合は、設定の記述や読み込みに問題がないか確認してください。
ただし、caddy validate は設定の読み込みやモジュールの初期化などを検証するものであり、バックエンドとの実際の通信や、公開環境での証明書発行が成功することまで保証するものではありません。
なお、一部のモジュールは検証時に排他的なリソースを確保するため、稼働中のCaddyに対する caddy validate が失敗する場合があります。その場合は、稼働中のプロセスに対して caddy reload を実行し、新しい設定を読み込めるか確認してください。読み込みに失敗した場合、通常は従来の設定で動作を継続します。
2. 稼働中のプロセスに設定を反映する (caddy reload)
検証が成功したら、設定を再読み込みします。
# systemd環境
sudo systemctl reload caddy
# Caddyの管理APIを利用して直接再読み込みする場合
sudo caddy reload --config /etc/caddy/Caddyfile
caddy validate は設定を検証するコマンドであり、稼働中の設定を変更するものではありません。設定を適用するには、caddy reload または環境に応じたサービス管理コマンドを使用してください。
運用時の重要な注意点
- 検証と反映は別の操作:
caddy validateは設定を読み込み、モジュールの初期化なども含めて検証しますが、設定を稼働中のプロセスに反映するコマンドではありません。 - 稼働中のCaddyへの反映:通常の設定変更では、
sudo systemctl reload caddyまたはsudo caddy reload --config /etc/caddy/Caddyfileを使用します。新しい設定の読み込みに失敗した場合、Caddyは従来の設定で動作を継続します。
Docker Composeで作るCaddyリバースプロキシ環境

docker-compose.ymlとCaddyfileの設定例:CaddyとNode.jsアプリを連携する
Docker Composeを利用すると、CaddyとバックエンドのNode.jsアプリケーションを同一のネットワーク上で手軽に起動・管理できます。ここでは、ローカル開発環境や小規模な本番環境でそのまま活用できる最小構成のファイル群を提示します。
ディレクトリ構成
プロジェクトルートディレクトリに、次のような構成でファイルを配置します。
my-caddy-app/
├── docker-compose.yml
├── conf/
│ └── Caddyfile
└── app/
├── Dockerfile
└── server.js
Caddyfileは conf/ ディレクトリに配置します。Docker Composeでは、ファイル単体ではなくディレクトリをマウントすることで、エディターによるファイルの置き換え時にも変更を反映しやすくなります。
1. Node.jsアプリケーションのサンプル (app/server.js)
簡単なHTTPサーバーを応答するNode.jsスクリプトです。
const http = require('http');
const server = http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
res.end('Hello from Node.js backend via Caddy reverse proxy!\n');
});
const PORT = 3000;
server.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
});
2. Node.jsのDockerfile (app/Dockerfile)
FROM node:26-alpine
WORKDIR /app
COPY server.js .
EXPOSE 3000
CMD ["node", "server.js"]
Node.js 26は、2026年10月28日時点でLTSとして提供されています。このサンプルでは node:26-alpine を使用しています。実際の運用では、採用するNode.jsのバージョンやAlpineイメージの互換性を確認したうえで、サポート中のバージョンを選択してください。
3. Caddyfileの設定 (Caddyfile)
ローカルでHTTPによる動作確認を行う場合は、サイトアドレスを :80 と指定します。
:80 {
reverse_proxy web-app:3000
}
:80 はポート80でHTTPリクエストを受け付ける指定です。この例ではローカルHTTPSへの自動切り替えを使わないため、HTTPによる疎通確認を簡単に行えます。
本番環境でHTTPSを利用する場合は、実際の公開ドメインを指定します。
example.com {
reverse_proxy web-app:3000
}
本番用の設定では、ドメインのDNS設定と外部からの到達性を整えたうえで、証明書の自動取得・更新が行えるようにします。
4. Docker Compose設定 (docker-compose.yml)
services:
caddy:
image: caddy:2-alpine
ports:
- "80:80"
- "443:443"
- "443:443/udp"
volumes:
- ./conf:/etc/caddy:ro
- caddy_data:/data
- caddy_config:/config
restart: unless-stopped
depends_on:
- web-app
web-app:
build: ./app
restart: unless-stopped
volumes:
caddy_data:
caddy_config:
./conf:/etc/caddy:ro は、ホスト側の conf/ ディレクトリをコンテナ内の /etc/caddy に読み取り専用でマウントする設定です。Caddyfileを編集した後は、設定の検証と再読み込みを行ってください。
443:443/udp はHTTP/3を利用する場合のUDPポート公開です。HTTP/3が不要な環境では省略できます。
バックエンドの web-app には ports を指定していません。同じComposeネットワークに接続されているため、Caddyから web-app:3000 で通信でき、ホスト側にバックエンドのポートを公開する必要はありません。
起動・動作確認・停止
1.コンテナのビルドと起動
docker-compose.yml があるディレクトリで、次のコマンドを実行します。
docker compose up -d --build
2.動作確認
ローカル環境用のCaddyfileを使用している場合は、ブラウザーまたはターミナルからアクセスします。
curl http://localhost正常に連携できていれば、Node.jsから次のメッセージが返ります。
Hello from Node.js backend via Caddy reverse proxy!
3.コンテナの停止
docker compose down
本番用のドメイン設定を使用する場合は、公開ドメインのDNSとポートの到達性を確認してからHTTPSの動作を検証してください。
Caddyコンテナと複数Webアプリコンテナのネットワーク連携
Docker Compose環境では、同一のComposeファイル内で定義されたサービス同士がデフォルトで同じDocker内部ネットワークに所属するため、コンテナ名(サービス名)をホスト名として相互通信が可能です。
1. コンテナ内の localhost とサービス名の違い
localhostの罠: Caddyコンテナの設定ファイル内でlocalhost:3000と指定した場合、それはCaddyコンテナ自身のループバックアドレス(ポート3000)を指してしまいます。Node.jsアプリが別のコンテナで動いている場合、Caddyからはアクセスできず「502 Bad Gateway」エラーになります。- サービス名の使用:
web-app:3000のように、docker-compose.ymlで定義したサービス名(またはコンテナ名)を宛先として指定することで、Dockerの内部DNSが自動的に適切なコンテナのIPアドレスへと名前解決してくれます。
2. 内部通信と外部公開の分離
セキュリティを高めるため、バックエンドのNode.jsコンテナのポートはホスト側に直接公開(ports ディレクティブの指定)する必要はありません。
services:
caddy:
image: caddy:2-alpine
ports:
- "80:80"
- "443:443"
# Caddyのみが外部からのポートを受け付ける
web-app:
build: ./app
# ports: は記述しない。ホスト側にバックエンドのポートを公開しない
同じネットワーク内のほかのコンテナからも通信できる点に注意する
3. 複数アプリの振り分け例(ドメイン別・パス別)
複数のバックエンドコンテナがある場合、Caddyfileで次のようにサービス名を宛先にしてルーティングを振り分けます。
※以下はCaddyfileの設定例です。service-app1 と service-app2 は例示用のサービス名であり、実際に動作させるには、Docker Compose側にもそれぞれのサービスを定義し、Caddyと同じDockerネットワークに接続する必要があります。
# アプリAへの振り分け(サブドメイン例、またはローカルホストのポート分岐)
app1.localhost {
reverse_proxy service-app1:3000
}
app2.localhost {
reverse_proxy service-app2:4000
}
Caddyfileと証明書データを永続化するボリューム設定
Dockerコンテナのライフサイクル(破棄と再作成)を考慮すると、設定ファイルやSSL/TLS証明書などの重要なデータはホスト側やDockerボリュームに永続化することが不可欠です。
1. Caddyにおける /data と /config の役割
CaddyのDocker環境では、証明書や設定状態をコンテナの再作成後も保持できるように、データの保存先を永続化します。
/data:TLS証明書、秘密鍵、OCSPステープルなど、Caddyの証明書管理や稼働に必要な重要データを保存します。永続化が特に重要なディレクトリです。/config:自動保存されたJSON設定など、Caddyの設定状態を保存するために使われます。Caddyfileそのものを格納するための必須ディレクトリではありません。
Composeファイルでは、次のように名前付きボリュームを割り当てます。
services:
caddy:
image: caddy:2-alpine
volumes:
- ./conf:/etc/caddy:ro
- caddy_data:/data
- caddy_config:/config
volumes:
caddy_data:
caddy_config:
docker compose down では名前付きボリュームは通常削除されませんが、docker compose down -v を実行すると、これらのボリュームも削除対象になります。本番運用では、証明書や秘密鍵を含むデータを不用意に消さないよう注意してください。
2. ボリューム運用の注意点
Caddyの証明書や設定状態を保持するには、コンテナの再作成時にも名前付きボリュームを維持することが重要です。
- コンテナの再作成(
docker compose down && docker compose up -d):通常、名前付きボリュームは削除されないため、caddy_dataに保存された証明書や秘密鍵などのデータを引き継げます。 - ボリュームの削除(
docker compose down -v):vオプションを付けると、Composeが管理する名前付きボリュームも削除対象になります。保存されていた証明書などのデータが失われるため、本番環境では実行前に対象を確認してください。
Caddy reverse proxyの応用設定:ヘッダー・HTTPS・セキュリティ

Let’s EncryptによるSSL/TLS自動取得とHTTPS自動リダイレクト
Caddyは、公開ドメインを使用してサーバーを稼働させる際、面倒な証明書の申請・インストール作業を意識することなく、自動的にHTTPSを有効化する優れた機能を持っています。
1. HTTPS自動化の前提条件
Caddyで公開ドメインのHTTPSを自動化するには、次の条件を確認してください。
- DNS設定:ドメインのAレコードやAAAAレコードが、実際にアクセスできるサーバーのIPアドレスを指していること。AAAAレコードを設定している場合は、IPv6経由でも適切に接続できることを確認します。
- ポートの到達性:通常のWebサイトでは、HTTPの80番ポートとHTTPSの443番ポートを外部から到達可能にしておく構成が一般的です。ただし、ACMEによる証明書のドメイン検証で必要になるポートは、利用するチャレンジ方式によって異なります。
- データの永続化:Docker環境では
/dataを永続化し、コンテナの再作成後も証明書や秘密鍵などの状態を保持できるようにします。
ACMEのHTTP-01チャレンジはポート80、TLS-ALPN-01チャレンジはポート443を使用します。一方、DNS-01チャレンジはDNSレコードを使ってドメインの所有確認を行うため、HTTP-01と同じポート要件ではありません。必要なポートやDNS設定を、利用するチャレンジ方式に合わせて確認してください。
2. Caddyfileにおける記述と自動HTTPSの挙動
Caddyfileのサイトアドレスに「localhost」ではなく実際のドメイン名を指定するだけで、Caddyは自動的にHTTPリクエストをHTTPSへリダイレクトし、セキュアな接続を提供します。
example.com {
# 明示的なリダイレクト設定を書かなくても、自動的にHTTPS(443)へリダイレクトされ、
# HTTP(80)からのアクセスは安全に転送されます。
reverse_proxy web-app:3000
}
3. ローカル環境と本番環境の違い
localhost や app.localhost をサイトアドレスにすると、CaddyはローカルHTTPS用の内部CAを使って証明書を発行できます。ただし、Dockerコンテナ内で動かしている場合、ホスト側のブラウザーやHTTPクライアントがそのCAを信頼していないと、証明書の警告が表示されることがあります。
ローカルでHTTPによる疎通だけを確認したい場合は、前述の :80 を利用した設定が簡単です。本番環境では公開ドメインを指定し、DNSと外部からの到達性を確認したうえで自動HTTPSを利用します。
X-Forwarded-ForとクライアントIPの扱い
リバースプロキシを経由すると、バックエンドから見た直接の接続相手はCaddyになることがあります。Caddyの reverse_proxy は、通常、次の転送ヘッダーを設定・更新します。
X-Forwarded-For:プロキシを経由するクライアントIPアドレスの情報。X-Forwarded-Proto:クライアントが利用した元のプロトコル。X-Forwarded-Host:クライアントが要求した元のホスト名。
バックエンドでは、これらのヘッダーを参照することで元のリクエスト情報を取得できます。ただし、X-Forwarded-For の値が常に単一の信頼できるクライアントIPを示すわけではありません。複数のプロキシを経由する場合は複数のIPが含まれることがあり、信頼できない接続元から送られたヘッダーを無条件に信頼するのは危険です。
ExpressなどのアプリケーションフレームワークでクライアントIPを取得する場合は、実際のネットワーク構成に応じて、信頼するプロキシを適切に設定してください。
カスタムヘッダーが必要な場合は、たとえば次のように追加できます。
example.com {
reverse_proxy web-app:3000 {
header_up X-Real-IP {remote_host}
}
}
ここで {remote_host} が示すのはCaddyに直接接続した相手です。前段にCloudflareなどのCDNやロードバランサーがある場合は、そのCDNやロードバランサーのIPアドレスになることがあります。その場合は、Caddyの trusted_proxies などを適切に設定し、信頼できるプロキシから受け取った情報に基づいてクライアントIPを判断してください。
パス指定(/api/* など)やサブドメインごとに異なるバックエンドへの割り当て方法
1台のサーバーやコンテナ群で複数のアプリケーションやAPIを公開する場合、ドメイン(サブドメイン)による振り分けや、URLのパス(プレフィックス)による振り分けを使い分けます。
1. サブドメインによる振り分け
もっともシンプルで推奨される方法です。ホスト名ごとに異なるバックエンドコンテナへルーティングします。
# フロントエンド用
app.example.com {
reverse_proxy frontend-app:8080
}
# APIサーバー用
api.example.com {
reverse_proxy backend-api:4000
}
2. handle と handle_path のルーティング
同じドメインで、/api/ から始まるリクエストをAPIサーバーへ転送し、それ以外をフロントエンドへ転送する場合は、次のように設定します。
example.com {
# /api/ のプレフィックスを取り除いてAPIへ転送
handle_path /api/* {
reverse_proxy backend-api:4000
}
# それ以外のリクエストをフロントエンドへ転送
handle {
reverse_proxy frontend-app:8080
}
}
handle_path は、指定したパスに一致するリクエストを処理する際に、マッチしたパスのプレフィックスを取り除きます。この例では /api/users へのリクエストが、バックエンドでは /users として処理されます。
一方、handle はマッチしたリクエストを処理しますが、URLのパスを自動では変更しません。バックエンドが /api/users というパスをそのまま期待している場合は、handle /api/* を使用してください。
Caddy reverse proxyが動かないときの原因と解決方法

502 Bad Gateway・接続拒否が発生する原因:バックエンド・ポート・Dockerネットワークの確認
Caddyを導入した際、ブラウザーに「502 Bad Gateway」が表示されたり、接続拒否(Connection Refused)が発生したりする場合、Caddy自体は正常に起動しているものの、Caddyからバックエンドのアプリケーションへ通信が届いていないことが主な原因です。
主な原因と切り分け手順
コンテナ内の localhost を宛先に指定しているミス
- 現象: Docker Compose環境などで、Caddyfileに
reverse_proxy localhost:3000と記述している。 - 理由: Caddyコンテナから見た
localhostはCaddy自身を指すため、別コンテナで動くNode.jsアプリに到達しません。 - 解決策: 宛先を
docker-compose.ymlで定義したサービス名(例:reverse_proxy web-app:3000)に変更します。
バックエンドアプリケーションの起動失敗やポートの不一致
- 現象: アプリケーション側でエラーが発生してクラッシュしている、またはバインドしているポート番号が異なっている。
- 解決策: バックエンドのコンテナやサーバーが正常に起動しているか、指定したポートでリッスンしているかを確認します。
Dockerネットワークの孤立
- 現象: Caddyとバックエンドアプリが異なるDockerネットワークに所属している。
- 解決策: 同一のDocker Composeファイル内で定義するか、共有ネットワーク(外部ネットワーク)に接続していることを確認します。
接続確認用コマンド
Caddyコンテナ内からバックエンドへ直接通信し、Node.jsアプリケーションが正常に応答するか確認します。
# Caddyコンテナ内からバックエンドへ疎通確認
docker compose exec caddy wget -qO- http://web-app:3000
正常に通信できれば、Node.jsから次のメッセージが返ります。
Hello from Node.js backend via Caddy reverse proxy!
公開ドメインのHTTP・HTTPSへの到達性を確認する場合は、次のコマンドを使用します。
# HTTPの応答確認
curl -I http://example.com
# HTTPSの応答確認
curl -I https://example.com
HTTPで応答が返っても、HTTPSや証明書の検証が正常に動作しているとは限りません。HTTPSでエラーが発生する場合は、Caddyのログや証明書の状態も確認してください。
403 Forbidden・404 Not Found・リダイレクトループの原因と対処法
HTTPステータスコード「403」「404」やリダイレクトループは、Caddyがバックエンドに到達した後のルーティングミスや、アクセス権限の設定不備に起因することが多くあります。
エラー別の原因と対処方法一覧
| 発生する症状 | 主な原因 | 確認・対処項目 |
|---|---|---|
| 403 Forbidden | 静的ファイルの読み取り権限不足、ディレクトリの通過権限不足、Caddyfileのアクセス制御、バックエンド側のアクセス拒否 | 静的配信ではファイルの読み取り権限だけでなく、親ディレクトリの通過権限やCaddyの実行ユーザーを確認する。プロキシの場合はバックエンドのログやアクセス制御設定も調べる |
| 404 Not Found | Caddyfileのパス指定の不一致、バックエンド側に該当するURLが存在しない | handle_path によるプレフィックス削除の有無と、バックエンドが受け付けるパスを照合する |
| リダイレクトループ | HTTP・HTTPSのリダイレクト設定の重複、前段CDNとCaddyのプロトコル設定の不整合 | CaddyfileとCDN側のリダイレクト、オリジンへの接続方式を確認する。Cloudflareを使用している場合はSSL/TLSモードも見直す |
パスプレフィックスの注意点(handle vs handle_path)
/api/* などのパスでルーティングを切り分ける際、バックエンドアプリが /api/users を期待しているのか、プレフィックスを取り除いた /users を期待しているのかによって使い分けが必要です。
handle: リクエストされたURL(/api/users)をそのままバックエンドへ転送します。handle_path: マッチしたプレフィックス(/api)を削除した上でバックエンドへ転送します。バックエンド側のルーティング設計に合わせて正しく選択してください。
Caddyのログを確認する方法:設定エラー・HTTPS証明書・DNSのトラブルを切り分ける
トラブルが発生した際は、勘に頼らずログを確認することで、問題の根本原因を素早く特定できます。
1. 環境別のログ確認コマンド
Docker環境で稼働している場合
# リアルタイムでCaddyコンテナのログを追跡
docker compose logs -f caddy
systemd(Linux直接インストール)で稼働している場合
# systemdのジャーナルログからCaddyのログを抽出
sudo journalctl -u caddy -e --no-pager
2. ログから読み解く主なトラブルシューティング
証明書発行エラー(ACME関連)
Caddyのログに証明書の取得やドメイン検証に関するエラーが出ている場合は、エラーメッセージ全体と、その前後のログを確認します。ログの内容は、利用する認証局やチャレンジ方式、実行環境によって異なります。
- DNS設定の確認:ドメインのAレコードやAAAAレコードが、実際にアクセスできるサーバーを指しているか確認します。AAAAレコードを設定している場合は、IPv6経由でも正しく接続できることが必要です。
- ポートの到達性の確認:HTTP-01チャレンジではポート80、TLS-ALPN-01チャレンジではポート443への到達性を確認します。ファイアウォールやルーター、クラウドのセキュリティグループなどで通信が遮断されていないか調べてください。
- DNS-01チャレンジの確認:DNSレコードによる検証を利用する場合は、必要なTXTレコードが正しく設定されることを確認します。Caddyで利用するDNSプロバイダーに応じて、対応するモジュールや認証情報の設定も必要です。
Caddyfileの構文・読み込みエラー
- ログの例:
adapt: parse error: unknown directive ... - 確認項目:
caddy validateコマンドを実行し、設定ファイルの記述ミスやタイポを修正します。
トラブルシューティングの進め方
- ログを確認する:Docker Compose環境では
docker compose logs -f caddy、systemd環境ではsudo journalctl -u caddy -e --no-pagerを実行し、エラーの原因を確認します。 - DNSと通信経路を確認する:
digやnslookupでDNSの名前解決を確認し、HTTP・HTTPSの到達性をテストします。 - 設定を検証して再読み込みする:設定ファイルを修正したら、実行環境に応じたコマンドで検証し、設定を反映します。
Docker Compose環境では、次のコマンドを使用します。
# Caddyfileの検証
docker compose exec caddy caddy validate --config /etc/caddy/Caddyfile
# 稼働中のCaddyへ設定を再読み込み
docker compose exec caddy caddy reload --config /etc/caddy/Caddyfile
systemd環境では、次のコマンドを使用します。
# Caddyfileの検証
sudo caddy validate --config /etc/caddy/Caddyfile
# 設定の再読み込み
sudo systemctl reload caddy
通常の設定変更では、コンテナ全体を再起動するよりも、caddy reload による設定の再読み込みを優先してください。
よくある質問(FAQ)
-
Caddyのリバースプロキシは無料で使用できますか?
-
Caddy本体はApache License 2.0で公開されているオープンソースソフトウェアで、商用利用も可能です。
Caddy本体の利用にライセンス購入は通常必要ありません。Let’s Encryptなどの認証局による証明書発行も、各認証局の利用条件に従って利用できます。ただし、外部サービスや追加プラグインを組み合わせる場合は、それぞれのライセンスや料金体系も確認してください。
-
CaddyとNginxはどちらを選ぶべきですか?
-
設定の簡潔さと自動HTTPSを重視するならCaddy、既存のNginx運用基盤や設定資産を活用したいならNginxが候補になります。
Caddyは、自動HTTPS機能と読みやすいCaddyfileによって、証明書管理やリバースプロキシの設定を簡潔にできます。一方、NginxもACMEクライアントなどと連携して証明書の取得・更新を自動化でき、柔軟なルーティングや既存の運用ノウハウを活用できます。どちらが適しているかは、必要な機能やチームの運用経験によって異なります。
-
バックエンドアプリケーションのポートを外部公開(ホストへマウント)する必要はありますか?
-
いいえ、外部公開する必要はありません。むしろセキュリティの観点から非公開にすることが推奨されます。
Docker Compose環境では、Caddyのみでホスト側の公開ポート(80番・443番)を受け付け、バックエンドコンテナには
portsを指定しない構成が一般的です。これにより、バックエンドのポートがホスト側に直接公開されることを防げます。ただし、同じDockerネットワーク内の別のコンテナなどからの通信まで自動的に遮断されるわけではありません。より厳密にアクセスを制限したい場合は、Dockerネットワークの分離なども検討してください。
-
Caddyで実際のクライアントIPを取得するにはどうすればよいですか?
-
Caddyが設定する転送ヘッダーを利用できますが、信頼するプロキシとアプリケーション側の設定を適切に管理する必要があります。
Caddyは通常、
X-Forwarded-For、X-Forwarded-Proto、X-Forwarded-Hostなどを設定・更新します。バックエンドで元のクライアントIPを取得したい場合は、これらのヘッダーを利用する方法があります。ただし、複数のプロキシを経由する構成では
X-Forwarded-Forに複数のIPアドレスが含まれることがあります。また、クライアントが任意の値を送信できる可能性も考慮しなければなりません。Caddyのtrusted_proxiesと、Expressなどのフレームワーク側の信頼プロキシ設定を、実際の構成に合わせて設定してください。
まとめ
Caddyは、シンプルで直感的なCaddyfileと、自動HTTPS機能を備えたWebサーバー兼リバースプロキシです。公開ドメインのDNS設定や外部からの到達性などの条件を満たせば、ACME認証局を利用したSSL/TLS証明書の取得・更新や、HTTPからHTTPSへのリダイレクトを自動化できます。また、ドメインやパスに応じたルーティングにも対応しているため、複数のWebアプリケーションを少ない設定で公開したい場合に適しています。
これまでの解説で見てきたように、実務や個人開発でCaddyを活用する際は、いくつかの要点を押さえておくことでスムーズな運用が可能になります。
Caddyを導入することで、インフラ管理の手間を最小限に抑え、本来のアプリケーション開発や機能実装へリソースを集中させることができます。ぜひ今回の設定例や解説を参考に、ご自身の開発・運用環境へCaddyを取り入れてみてください。
TVCMで話題の【ココナラ】無料会員登録はこちら


