深夜2時、Nginxの設定が壊れた
こんな経験はないだろうか。新しいサービスをリリースしたばかり、クライアントは別のタイムゾーンで起きていて、1時間前にはなかった502 Bad Gatewayが画面に表示されている。/etc/nginx/sites-available/を開いてバーチャルホストブロックを見比べ始め、新しい設定を追加する際に動いていた設定を誤って上書きしてしまったことに気づく。
私自身、認めたくないほど何度もこの状況に陥った。SSL証明書の管理、ポートルーティング、サブドメインのマッピングなど、複数のリバースプロキシ設定を手動で管理するのは、十分に休息を取った状態でもミスが起きやすい。そこでNginx Proxy Managerを使い始めたのだが、気づけばスタックに欠かせないツールの一つになっていた。
生のNginx設定がリスクになる理由
問題はNginx自体ではなく、時間とともに積み重なる運用コストだ。サービスが1つなら設定ファイルが数個あっても管理できる。しかしリバースプロキシの背後に5つ、10個、20個とサービスが増えてくると、綻びが見え始める。
- Let’s Encryptの証明書は90日ごとに期限切れになる。Certbotの更新には手動設定かcronの自動化が必要で、失敗しても気づかず、ユーザーがブラウザで証明書エラーを見るまで誰も知らない。
- 新しいチームメンバーは、他のサービスに影響を与えるリスクを冒さずにnginxの設定を触れない。
- 監査ログがない——誰が何をいつ変更したかわからない。
- 設定変更のテストは
nginx -tを実行して、リロード前にすべて確認できていると祈るだけだ。
設定ファイルが本当の敵ではない。欠けているのは、安全で構造化された管理インターフェースだ——特にチームや多サービス環境では。
解決策の比較:一般的なアプローチ
オプション1:Nginx設定ファイルの手動管理
クラシックなアプローチだ。完全なコントロール、抽象化レイヤーなし、最大限の柔軟性がある。欠点は上述した通り。1人で使う分には問題ないが、規模が大きくなると辛くなる。
オプション2:Traefik
TraefikはDockerを多用するスタックで人気がある。Dockerラベルを通じてサービスを自動検出し、SSLを自動で処理する。ただし、Traefikのメンタルモデル——ミドルウェア、ルーター、エントリポイント——で考える必要がある。設定はYAMLまたはTOMLで、何か問題が起きたときのデバッグは楽しくない。また、新規のDocker ComposeやKubernetesセットアップに向いている。既存のベアメタルサービスを管理するなら、無理やり合わせようとしている感覚になりかねない。
オプション3:Caddy
Caddyはシンプルで、標準で自動HTTPSに対応している。CaddyfileのシンタックスはNginxよりはるかにシンプルで、基本的なリバースプロキシはわずか3行で書ける。シンプルなルーティングでゼロから始めるなら、Caddyは検討する価値がある。ただし、管理UIがなく、高度なルーティングシナリオになるとすぐにプラグインエコシステムに踏み込む必要がある。
オプション4:Nginx Proxy Manager (NPM)
NPMはDockerコンテナとして動作し、NginxをクリーンなWeb UIでラップする。プロキシホストはブラウザのインターフェースから追加できる。SSL証明書はLet’s Encrypt経由で発行・自動更新される。内部のnginx設定は自動生成される。サブドメインのルーティング、HTTPSの強制、アクセス制御の管理など、実際のユースケースの90%を設定ファイルを一切触らずにカバーできる。
Nginx Proxy Managerのセットアップ
前提条件
- DockerとDocker ComposeがインストールされたサーバーまたはVPS
- ファイアウォールでポート80、443、81を開放
- サーバーを指すDNSレコードが設定されたドメイン
ステップ1:Docker Composeファイルの作成
NPM用のディレクトリを作成してdocker-compose.ymlを配置する:
mkdir -p ~/nginx-proxy-manager && cd ~/nginx-proxy-manager
version: '3.8'
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
- '80:80'
- '443:443'
- '81:81'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
ポート81は管理用Web UIだ。ポート80と443が実際のトラフィックを処理する。volumesセクションはコンテナ再起動時にプロキシ設定とSSL証明書を保持する。これを省略すると、コンテナを更新した際にセットアップ全体が消えてしまう。
ステップ2:コンテナの起動
docker compose up -d
docker compose logs -f app
ログを確認しよう。初回起動時はSQLiteデータベースの初期化に10〜15秒ほどかかる。準備完了のメッセージが表示されたら使用可能だ。
ステップ3:管理パネルへのログイン
ブラウザでhttp://your-server-ip:81を開く。デフォルトの認証情報:
- メール:
[email protected] - パスワード:
changeme
初回ログイン時に変更を求められる。すぐに変更すること——そして管理ユーザーの設定が完了したら、ポート81をファイアウォールで外部からのアクセスをブロックしよう。
ステップ4:最初のプロキシホストの追加
Proxy Hosts → Add Proxy Hostに移動する。以下を入力:
- Domain Names:例、
app.yourdomain.com - Scheme:上流サービスに応じて
httpまたはhttps - Forward Hostname/IP:サービスの内部IPまたはコンテナ名
- Forward Port:サービスがリッスンするポート
SSLタブに切り替える。Request a new SSL Certificateを選択し、Force SSLとHTTP/2 Supportにチェックを入れ、Let’s Encrypt用のメールアドレスを入力して保存する。NPMが証明書の発行と自動更新を処理してくれる——certbotのcronジョブは不要だ。
ステップ5:同じDockerネットワーク上のサービスの扱い
同じホスト上でDockerサービスが動いている場合は、NPMがIPではなくコンテナ名でアクセスできるよう、両方のコンテナを共有ネットワークに配置しよう:
version: '3.8'
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
restart: unless-stopped
ports:
- '80:80'
- '443:443'
- '81:81'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
- proxy
networks:
proxy:
name: proxy
driver: bridge
次に、別サービスのComposeファイルでネットワークをexternalとして宣言する:
networks:
proxy:
external: true
NPMのプロキシホスト設定では、コンテナ名をforward hostnameとして使用する。サービスのポートは内部に留まり、ホストには何も公開されない。
ステップ6:アクセスリストによるアクセス制限
NPMはAccess Listsを通じてBasic認証とIPホワイトリストをサポートしている。GrafanaダッシュボードやPortainer、管理パネルなどの内部専用サービスには、Access Listsでアクセスリストを作成し、IPレンジまたはHTTP認証情報を追加してプロキシホストに適用しよう。内部ツールを誤ってインターネットに公開することなく、クリーンなHTTPSサブドメインが使える。
注意すべき点
NPMは完璧ではない。コンテナは内部的にrootで動作する——セキュリティ要件が厳しい場合は把握しておくべきだ。SQLiteデータベース(設定すればMariaDB)にすべてのプロキシ設定が保存されるため、バックアップを取ること。アップグレード前にスナップショットしておきたいのは./dataボリュームだ。
カスタムのnginxディレクティブ、レート制限、特定のヘッダー操作が必要な場合は?NPMのAdvancedタブでプロキシホストごとに生のnginx設定スニペットを注入できる。生のNginxのすべての機能は使えないが、UIを完全に諦めることなくほとんどのエッジケースに対応できる。
VPS上で10以上のサービスを運用しながら、まだ設定ファイルを手動で編集しているなら?それは取り戻せない時間だ。深夜2時にssl_certificateのパスをデバッグしなくて済む時間の節約は、思っているより早く積み重なっていく。
クイックリファレンス
- 管理UI:
http://server-ip:81 - デフォルトログイン:
[email protected]/changeme - SSL証明書の保存場所:
./letsencrypt - プロキシ設定の保存場所:
./data - NPMの更新:
docker compose pull && docker compose up -d
すでに3〜4つ以上のサービスでNginxを手動管理しているなら、週末にステージングサーバーでNPMを試してみよう。既存のスタックと並行してセットアップすればいい。必要なことの95%を、保守コストの何分の一かで処理してくれるはずだ。

