Webサーバーを選ぶ
WEB SERVER / CADDY

CaddyでWebページとPHPを配信する

Caddyを選び、静的ページの配信とPHP-FPMへの接続を確認します。

01 / Caddyを選び、ポートを確認する

このページはUbuntu Server 22.04の新規専用サーバーへCaddyを導入する手順です。物理マシン・仮想マシンの操作は共通です。Caddy、Apache、Nginxはいずれかを選ぶ選択肢で、順番にすべてインストールする手順ではありません。

管理者とSSH、ファイアウォール、OS更新を済ませ、sudoと復旧コンソールを確保します。以降のコマンドは特記以外サーバーの一般管理者で実行します。

sudo ss -lntup
systemctl is-active nginx apache2 caddy
sudo ufw status verbose
sudo test ! -e /var/www/caddy-guide

80・443番がほかのWebサーバーやコンテナーで使われている場合は中断します。既存サイトを無条件に停止せず、別サーバーを使うか、切替時刻・バックアップ・復旧方法を設計してください。既にCaddyを運用している構成への上書きは対象外です。

到達点はHTTPでのページ表示とPHP実行です。ドメインが未定でも進められるよう、最初は明示的なHTTPサイトにします。HTTPでログイン情報や秘密情報を外部へ送りません。CLIENT_IPはサーバーから見える確認端末の送信元IP、SERVER_IPは端末から到達できるサーバーのアドレスへ置き換えます。

02 / 公式stableパッケージとHTTP配信を用意する

Caddy公式のDebian/Ubuntu向け案内からstableリポジトリの入手先を確認します。Ubuntu標準リポジトリの古いパッケージと、この公式stable配布を混ぜません。TLSやAPTの署名検証は無効にしないでください。

sudo apt-get update && sudo apt-get install curl gnupg debian-keyring debian-archive-keyring apt-transport-https
sudo test ! -e /usr/share/keyrings/caddy-stable-archive-keyring.gpg
sudo test ! -e /etc/apt/sources.list.d/caddy-stable.list
WORKDIR="$(mktemp -d)"
curl --fail --show-error --location \
  --output "$WORKDIR/caddy.key" https://dl.cloudsmith.io/public/caddy/stable/gpg.key
curl --fail --show-error --location \
  --output "$WORKDIR/caddy-stable.list" https://dl.cloudsmith.io/public/caddy/stable/debian.deb.txt
gpg --show-keys --with-fingerprint "$WORKDIR/caddy.key"
cat "$WORKDIR/caddy-stable.list"

同名の鍵・リポジトリ設定がある場合や取得に失敗した場合は、以下へ進まず既存構成を確認します。鍵の入手先とリポジトリURLを公式案内と照合し、指紋を記録します。指紋を表示しただけでは鍵の所有者を独立に確認したことにはなりません。リポジトリ設定のsigned-byが、以下で保存する専用鍵束を指定していることも確認してください。

sudo test ! -e /usr/share/keyrings/caddy-stable-archive-keyring.gpg &&
sudo test ! -e /etc/apt/sources.list.d/caddy-stable.list &&
sudo gpg --dearmor --output /usr/share/keyrings/caddy-stable-archive-keyring.gpg "$WORKDIR/caddy.key" &&
sudo install -o root -g root -m 644 "$WORKDIR/caddy-stable.list" /etc/apt/sources.list.d/caddy-stable.list &&
sudo chmod 644 /usr/share/keyrings/caddy-stable-archive-keyring.gpg
sudo apt-get update && sudo apt-get install caddy
caddy version

APTの署名エラーがないことを確認します。既定のサービスが起動するため、最初のポート確認が必要です。設定と配信ファイルを用意します。ファイルは管理者が更新し、Caddyへ書き込み権限を渡しません。

BACKUP="$(sudo mktemp -d /root/server-guide-caddy.XXXXXX)" &&
sudo cp -a /etc/caddy "$BACKUP/"
printf '保存先: %s\n' "$BACKUP"
sudo test ! -e /var/www/caddy-guide &&
sudo install -d -o root -g root -m 755 /var/www/caddy-guide/public
sudo test ! -e /var/www/caddy-guide/public/index.html &&
printf '%s\n' '<!doctype html><html lang="ja"><meta charset="utf-8"><title>Caddy Guide</title><h1>Caddy is ready</h1></html>' | sudo tee /var/www/caddy-guide/public/index.html >/dev/null
sudo chown root:root /var/www/caddy-guide/public/index.html
sudo chmod 644 /var/www/caddy-guide/public/index.html

sudoedit /etc/caddy/Caddyfileで、新規サイト用として次の内容へ変更します。http://:80はHTTPを明示する指定で、この構成で公開証明書を自動取得するわけではありません。PHPをまだ接続しない段階ではPHP URLを404にして、ソースを配信しません。

http://:80 {
    root * /var/www/caddy-guide/public
    route {
        @hidden path_regexp hidden (^|/)\.
        respond @hidden 403
        @config path /wp-config.php
        respond @config 403
        @php path *.php
        respond @php 404
        file_server
    }
}
sudo caddy fmt --overwrite /etc/caddy/Caddyfile
sudo -u caddy -H caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile &&
sudo systemctl enable --now caddy && sudo systemctl reload caddy
systemctl is-active caddy
systemctl is-enabled caddy
curl --fail --show-error http://127.0.0.1/

構成の検証が成功し、サービスがactive・enabledで、Caddy is readyを含むHTMLが返ることを確認します。

03 / PHP-FPMをUnixソケットで接続する

PHPが不要な静的サイトならこの節を省略し、前節のPHP拒否設定を残します。PHPを使う場合はUbuntu 22.04のPHP 8.1を導入します。上流のPHP 8.1は保守終了済みで、Ubuntuによるパッケージ更新とアプリの互換性は別途確認が必要です。新しいPHPが必要なら対応するOS・パッケージ構成を選んでください。

sudo apt-get update && sudo apt-get install php8.1-fpm php8.1-cli
sudo systemctl enable --now php8.1-fpm
systemctl is-active php8.1-fpm
sudo ls -l /run/php/php8.1-fpm.sock
sudo usermod -a -G www-data caddy
id caddy

標準のFPMソケットはwww-dataグループから接続できる構成です。専用サーバーではCaddyのサービスユーザーを補助グループへ追加し、後でサービスを再起動して反映します。reloadだけでは新しいグループを読み直しません。共有環境では専用FPMプールとグループを設計し、ソケットを777へ変えたりTCP 9000番を外部公開したりしないでください。

静的設定を退避してから、/etc/caddy/Caddyfileを以下へ変更します。routeで非公開パスの拒否をPHP処理より先に固定します。php_fastcgiとfile_serverはセットで使います。

BACKUP="$(sudo mktemp -d /root/server-guide-caddy-php.XXXXXX)" &&
sudo cp -a /etc/caddy/Caddyfile "$BACKUP/Caddyfile"
printf '保存先: %s\n' "$BACKUP"
http://:80 {
    root * /var/www/caddy-guide/public
    route {
        @hidden path_regexp hidden (^|/)\.
        respond @hidden 403
        @config path /wp-config.php
        respond @config 403
        php_fastcgi unix//run/php/php8.1-fpm.sock
        file_server
    }
}
sudo caddy fmt --overwrite /etc/caddy/Caddyfile
sudo -u caddy -H caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile &&
sudo systemctl restart caddy
systemctl is-active caddy

検証と再起動が成功してから、次節の一時PHPファイルを置きます。Nginx専用のfastcgi_passやApacheの設定をCaddyfileへ貼り付けません。

04 / 別端末の表示とPHP実行・拒否を確認する

UFWを使っている場合は管理用SSH許可を維持し、まず確認端末だけを許可します。クラウドのFWやNATも確認してください。

sudo ufw allow from CLIENT_IP to any port 80 proto tcp
sudo ufw status verbose

別端末からhttp://SERVER_IP/でページが表示されるか確認します。PHPを導入した場合は、サーバー上でバージョンだけを返す一時ファイルを作ります。phpinfo()は使いません。

sudo test ! -e /var/www/caddy-guide/public/health.php &&
sudo tee /var/www/caddy-guide/public/health.php >/dev/null <<'PHP'
<?php
header('Content-Type: text/plain; charset=UTF-8');
echo 'PHP ' . PHP_VERSION . "\n";
PHP
sudo chown root:root /var/www/caddy-guide/public/health.php
sudo chmod 644 /var/www/caddy-guide/public/health.php
curl --fail --show-error http://127.0.0.1/health.php

別端末でもhttp://SERVER_IP/health.phpを開き、PHPの版が返り、ソースがダウンロードされないことを確認します。ソースが見えた場合は公開を止め、設定対象とPHP連携を修正してください。確認後はファイルを削除し、削除済み・未存在のPHPが404になることを確認します。

sudo rm /var/www/caddy-guide/public/health.php
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1/health.php
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1/server-guide-missing.php
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1/wp-config.php
curl -sS -o /dev/null -w '%{http_code}\n' http://127.0.0.1/.git/config

前の2件が404、非公開パスの後ろ2件が403であることが確認の目安です。HTTPステータス表示のcurlは、403/404でもcurl自体は正常終了する場合があります。これはWordPressの設置手順ではなく、保護ルールの確認です。

参考:動作確認と撮影条件

Ubuntu Server 22.04.5の実VMで公式stable版Caddy 2.11.7・PHP 8.1.2-1ubuntu2.28を使用しました。構成検査、active・enabled、サーバー内と別クライアントからのHTTP 200、静的段階のPHP URL拒否、Unixソケット経由のPHP実行を確認しました。FPM停止時は502でソースを返さず、復旧後は再実行できました。確認用PHPの削除後と存在しないPHPは404、非公開パスは403、再起動後の配信とlocalhost:2019の管理API待受も確認しています。

CaddyとPHP-FPMの実行確認画面
検証VMの実画面。図のHTTPSは、HTTP構成とは別に検証専用のローカルTLSサイトを追加した試験です。

05 / HTTPS・ログ・戻し方を確認する

Caddyは条件を満たすドメイン名のサイトで自動HTTPSを利用できます。ただし、上の明示的なHTTP構成は自動HTTPSではありません。公開時は所有するドメイン、正しいA/AAAAレコード、外部から80・443番へ到達する経路を用意し、証明書発行・HTTPS応答・更新ログを別途確認します。HTTP/3も使う場合はUDP 443番の経路も検討します。

ドメインを使って公開する場合は、前のCaddyfileの先頭 http://:80 { を、所有する実ドメインの example.com { に変更し、route内の保護とPHP設定を維持します。example.comは説明用なので、そのまま実行しません。サービスユーザーで検証してからreloadし、発行結果をログとブラウザーで確認します。reload成功だけでは証明書発行の完了とはいえません。この公開用変更は未検証で、公式資料に基づく案内です。

sudo -u caddy -H caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile &&
sudo systemctl reload caddy
curl -I http://example.com/
curl -I https://example.com/

証明書エラーを無視せず、名前・期限・チェーンとHTTPからHTTPSへの転送を確認します。通常のパッケージ構成では証明書等の状態は/var/lib/caddy配下に保存されます。秘密鍵を含むため公開せず、安全なバックアップと権限を維持します。管理APIの2019番はlocalhostで使い、外部へ開放しません。

追加の実測では、検証用のcaddy-guide.testにtls internalを設定し、CaddyのローカルCAによるTLSとPHP応答を確認しました。CAとホスト名の検証を有効にしたcurlは成功し、未信頼CAは終了コード60、未登録のSNI名はTLSハンドシェイクの終了コード35で拒否されました。これは公開信頼される証明書や実ドメインのACME発行とは別の試験です。本文の明示HTTP構成だけでTLSが有効になるわけではありません。

利用するドメインは未定のため、Caddyによる公開証明書の発行・自動更新は未検証です。ローカルHTTPが表示できたことを、HTTPSやインターネット公開まで完了したとは扱いません。参考:Caddyの自動HTTPS、php_fastcgi。

sudo journalctl -u caddy -b --no-pager
sudo journalctl -u php8.1-fpm -b --no-pager
sudo caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile
sudo ss -lntup

接続できない場合は待受、経路、FWを確認します。PHPが502等ならFPMの状態、ソケット名、所有者・グループとid caddyを確認し、補助グループの変更後にCaddyを再起動したかを調べます。ログには利用者のIP等が残るため、そのまま公開資料へ載せません。

PHP追加だけを戻す場合は一時ファイルを消し、退避した静的Caddyfileを戻して、サービスユーザーで検証後にreloadします。静的構成のPHP URL拒否を残し、PHPファイルをそのまま静的配信しません。サーバーの切替全体を戻す場合は新サービスを停止し、保管した旧構成と待受ポートを照合して元のサービスを復旧します。HTTPSを使うアプリでは、HTTPだけの構成へ戻したまま認証情報を送らないでください。

既存のPHP連携とHTTPSのページはNginx向けです。Caddyの設定へそのまま適用せず、上の公式資料と自分の構成に合わせて判断します。