Docker + Nginx + Let’s Encryptで構築する自動SSL更新リバースプロキシ完全ガイド

目次

はじめに

最近、ChatGPT、Claude、Google Geminiなど、AI開発支援サービスの普及により、プログラミング経験が浅い方でも本格的なWebアプリケーションを開発・公開できるようになりました。しかし、これらのアプリケーションを安全に運用するには、適切なセキュリティ対策が不可欠です。

そこで注目したいのが「リバースプロキシ」という技術です。特に、SSL/TLS証明書の自動更新機能を備えたリバースプロキシは、個人開発者や小規模チームにとって非常に重要なツールとなっています。

リバースプロキシとは?

リバースプロキシ概念説明図

リバースプロキシ:Webアプリケーションの賢い門番

ユーザー(お客様)
Webサイトにアクセス
HTTPSで安全に通信
Security
SSL
リバースプロキシ
(賢い門番)
• 不正アクセスをブロック
• HTTPS暗号化
• 負荷分散・キャッシュ
Webアプリケーション
サーバー
内部ネットワークで
安全に動作
セキュリティ強化
実際のサーバーのIPアドレス隠蔽、DDoS攻撃の緩和、不正アクセスのブロック
自動SSL管理
Let’s Encryptによる無料SSL証明書の自動取得・更新、TLS 1.2/1.3対応
運用効率化
複数アプリケーションの一元管理、アクセスログの集中管理、ヘルスチェック機能
パフォーマンス向上
静的コンテンツのキャッシュ、gzip圧縮、HTTP/2による高速通信、負荷分散

リバースプロキシは、簡単に言えば「Webアプリケーションの前に立つ賢い門番」のようなものです。

例えば:

  • お客様(ユーザー)がWebサイトにアクセスする時、最初に門番(リバースプロキシ)が応対します
  • 門番は不審な訪問者を検知したり、アクセスの集中を管理したりします
  • お客様とのやり取りを暗号化(HTTPS化)して、情報の盗聴を防ぎます
  • 複数のアプリケーションへのアクセスを適切に振り分けます

なぜリバースプロキシが必要?

1. セキュリティの強化

  • 実際のアプリケーションサーバーのIPアドレスやポート番号を隠蔽
  • 不正なアクセスをブロック
  • HTTPSによる通信の暗号化(TLS 1.2/1.3対応)
  • DDoS攻撃の緩和

2. 運用管理の効率化

  • 複数のアプリケーションを一つのドメインで管理
  • SSL証明書の一元管理(自動更新対応)
  • アクセスログの集中管理
  • ヘルスチェック機能

3. パフォーマンスの向上

  • 静的コンテンツのキャッシュ
  • gzip圧縮によるデータ転送量の削減
  • HTTP/2による高速通信
  • 負荷分散(ロードバランシング)

このガイドで実現できること

このガイドでは、2025年最新のベストプラクティスに基づいて、以下の環境を構築します:

無料のSSL証明書を自動更新する機能(Let’s Encrypt使用)
Docker Compose V2を使用した最新の構成
TLS 1.2/1.3による強固なセキュリティ
初心者でも理解できる詳細な手順説明
トラブルシューティングの充実したサポート

システム構成

このプロジェクトは、Docker ContainerでNginxとCertbotを動かし、安全で自動化されたWebサーバー環境を実現します。

主要コンポーネント

1. Nginxコンテナ(リバースプロキシ)

  • 外部からのアクセスを受け付け、内部のアプリケーションサーバーに転送
  • HTTPSの終端点として機能(SSL/TLSターミネーション)
  • Alpine Linux 3.20ベースの軽量イメージを使用(約10MB)
  • HTTP/2対応で高速通信を実現

2. Certbotコンテナ(SSL証明書管理)

  • Let’s Encryptから無料のSSL証明書を取得
  • 証明書の自動更新を担当(12時間ごとに確認、30日前から更新可能)
  • 更新時にNginxに自動で反映(ダウンタイムなし)
  • ACME v2プロトコル対応

設定ファイルの役割

ファイル名役割重要度
nginx.confNginxの基本設定(ワーカープロセス、ログ形式など)★★★
default.confサイト固有の設定(SSL設定、プロキシルール等)★★★
docker-compose.ymlコンテナの構成管理(Docker Compose V2形式)★★★
init-letsencrypt.sh初期セットアップ用スクリプト★★★
.env環境変数(ドメイン名、メールアドレスなど)★★☆

セキュリティ対策(2025年最新版)

このシステムには以下の最新セキュリティ機能が組み込まれています:

1. 通信の暗号化

# TLS 1.2と1.3のみを使用(古いプロトコルは無効化)
ssl_protocols TLSv1.2 TLSv1.3;

# 最新の暗号化スイート(2025年推奨)
ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384';

# Perfect Forward Secrecy(前方秘匿性)の確保
ssl_prefer_server_ciphers off;

2. HTTPセキュリティヘッダー

# HSTS(HTTP Strict Transport Security)
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

# その他のセキュリティヘッダー
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;

3. 自動証明書管理

  • SSL証明書の有効期限を自動監視
  • 期限30日前から自動更新開始
  • 更新失敗時の自動リトライ機能
  • 証明書の権限管理(600権限で保護)

4. アクセス制御

  • HTTPからHTTPSへの自動リダイレクト(301 Permanent Redirect)
  • レート制限機能(DDoS対策)
  • IPアドレスベースのアクセス制限(オプション)

前提条件と環境準備

必要な環境

セットアップを始める前に、以下の環境が整っていることを確認してください:

1. サーバー要件

  • OS: Ubuntu 22.04 LTS / Debian 12 / CentOS 9 / Rocky Linux 9 など
  • メモリ: 最小512MB(推奨1GB以上)
  • ストレージ: 最小10GB(ログファイル用の余裕を含む)
  • CPU: 1コア以上

2. 必要なソフトウェア

  • Docker Engine: v24.0以上(2025年1月時点の最新版推奨)
  • Docker Compose: v2.20以上(プラグイン版)

3. ネットワーク要件

  • パブリックIPアドレス(固定IPが望ましい)
  • ポート80と443が開放されていること
  • ファイアウォール設定が適切であること

4. ドメイン要件

  • 独自ドメイン(例:example.com)
  • DNSのAレコードがサーバーのIPアドレスに向いていること

Docker と Docker Compose のインストール

Ubuntu/Debian の場合:

# 1. 必要なパッケージをインストール
sudo apt-get update
sudo apt-get install -y ca-certificates curl gnupg

# 2. Docker公式のGPGキーを追加
sudo install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg
sudo chmod a+r /etc/apt/keyrings/docker.gpg

# 3. Dockerリポジトリを設定
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \
  $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null

# 4. Docker Engineをインストール
sudo apt-get update
sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin

# 5. 動作確認
docker --version
docker compose version  # "docker compose" にスペースが入ることに注意!

現在のユーザーをdockerグループに追加(sudoなしで実行可能にする):

sudo usermod -aG docker $USER
# 再ログインして変更を反映
newgrp docker

DNS設定の確認

ドメインが正しくサーバーに向いているか確認します:

# Aレコードの確認
dig +short あなたのドメイン.com

# または
nslookup あなたのドメイン.com

サーバーのIPアドレスが表示されれば設定は正しいです。

主要ファイルの詳細説明

docker-compose.yml(Docker Compose V2形式)

# Docker Compose V2仕様に準拠
services:  # 'version'の指定は不要になりました(v2.20以降)
  nginx-proxy:
    image: nginx:alpine
    container_name: nginx-proxy
    restart: unless-stopped  # 自動再起動の設定
    ports:
      - "80:80"
      - "443:443"
    volumes:
      # Nginx設定ファイル
      - ./nginx.conf:/etc/nginx/nginx.conf:ro
      - ./default.conf:/etc/nginx/conf.d/default.conf:ro
      # SSL証明書(Certbotと共有)
      - ./data/certbot/conf:/etc/letsencrypt:ro
      - ./data/certbot/www:/var/www/certbot:ro
    networks:
      - webproxy
    depends_on:
      - certbot
    # Nginxの自動リロード(証明書更新時)
    command: "/bin/sh -c 'while :; do sleep 6h & wait $${!}; nginx -s reload; done & nginx -g \"daemon off;\"'"

  certbot:
    image: certbot/certbot:latest
    container_name: certbot
    restart: unless-stopped
    volumes:
      - ./data/certbot/conf:/etc/letsencrypt:rw
      - ./data/certbot/www:/var/www/certbot:rw
    networks:
      - webproxy
    # 自動更新の設定(89日ごと = Let's Encryptの推奨間隔)
    entrypoint: "/bin/sh -c 'trap exit TERM; while :; do certbot renew; sleep 89d & wait $${!}; done;'"

networks:
  webproxy:
    driver: bridge
    ipam:
      config:
        - subnet: 172.20.0.0/16

default.conf(Nginx設定ファイル)

# HTTPサーバー設定(ポート80)
server {
    listen 80;
    listen [::]:80;  # IPv6対応
    
    server_name example.com www.example.com;
    
    # Let's Encryptのチャレンジファイル用
    location /.well-known/acme-challenge/ {
        root /var/www/certbot;
    }
    
    # その他のリクエストはHTTPSにリダイレクト
    location / {
        return 301 https://$host$request_uri;
    }
}

# HTTPSサーバー設定(ポート443)
server {
    listen 443 ssl http2;
    listen [::]:443 ssl http2;  # IPv6対応
    
    server_name example.com www.example.com;
    
    # SSL証明書の設定
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    
    # SSL設定(2025年最新版)
    ssl_session_timeout 1d;
    ssl_session_cache shared:SSL:10m;
    ssl_session_tickets off;
    
    # TLSバージョン(1.2と1.3のみ)
    ssl_protocols TLSv1.2 TLSv1.3;
    
    # 暗号化スイート(推奨設定)
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
    ssl_prefer_server_ciphers off;
    
    # OCSP Stapling(証明書の有効性確認を高速化)
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
    
    # セキュリティヘッダー
    add_header Strict-Transport-Security "max-age=63072000" always;
    add_header X-Frame-Options "SAMEORIGIN" always;
    add_header X-Content-Type-Options "nosniff" always;
    
    # バックエンドサーバーへのプロキシ設定
    location / {
        proxy_pass http://10.0.0.37:8080;  # バックエンドサーバーのアドレス
        
        # プロキシヘッダーの設定
        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;
        
        # WebSocket対応(必要に応じて)
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
        
        # タイムアウト設定
        proxy_connect_timeout 60s;
        proxy_send_timeout 60s;
        proxy_read_timeout 60s;
    }
}

nginx.conf(Nginx基本設定)

user nginx;
worker_processes auto;  # CPUコア数に応じて自動調整
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
    worker_connections 1024;  # 同時接続数
    use epoll;  # Linux向けの高性能イベントモデル
    multi_accept on;
}

http {
    include /etc/nginx/mime.types;
    default_type application/octet-stream;
    
    # ログフォーマット
    log_format main '$remote_addr - $remote_user [$time_local] "$request" '
                    '$status $body_bytes_sent "$http_referer" '
                    '"$http_user_agent" "$http_x_forwarded_for"';
    
    access_log /var/log/nginx/access.log main;
    
    # パフォーマンス設定
    sendfile on;
    tcp_nopush on;
    tcp_nodelay on;
    keepalive_timeout 65;
    types_hash_max_size 2048;
    client_max_body_size 100M;  # アップロードサイズ制限
    
    # Gzip圧縮
    gzip on;
    gzip_vary on;
    gzip_min_length 1024;
    gzip_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml application/atom+xml image/svg+xml text/x-js text/x-cross-domain-policy application/x-font-ttf application/x-font-opentype application/vnd.ms-fontobject image/x-icon;
    
    # セキュリティ設定
    server_tokens off;  # Nginxバージョンを隠す
    
    # サイト固有の設定を読み込み
    include /etc/nginx/conf.d/*.conf;
}

init-letsencrypt.sh(初期セットアップスクリプト)

#!/bin/bash

# カラー出力用の変数
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m' # No Color

# 設定変数(環境に応じて変更してください)
domains=(example.com www.example.com)  # ドメイン名(複数可)
rsa_key_size=4096  # RSA鍵のサイズ
data_path="./data/certbot"
email="admin@example.com"  # Let's Encryptからの通知用メールアドレス
staging=1  # 1=テストモード、0=本番モード

# Docker Composeの確認
if ! [ -x "$(command -v docker)" ]; then
  echo -e "${RED}エラー: Dockerがインストールされていません。${NC}" >&2
  exit 1
fi

if ! docker compose version >/dev/null 2>&1; then
  echo -e "${RED}エラー: Docker Compose V2がインストールされていません。${NC}" >&2
  exit 1
fi

# 既存のデータ確認
if [ -d "$data_path" ]; then
  read -p "既存のデータが見つかりました。続行しますか? (y/N) " decision
  if [ "$decision" != "Y" ] && [ "$decision" != "y" ]; then
    exit
  fi
fi

# ダミー証明書の作成(Nginx起動用)
echo -e "${GREEN}### ダミー証明書を作成中...${NC}"
path="/etc/letsencrypt/live/${domains[0]}"
mkdir -p "$data_path/conf/live/${domains[0]}"
docker compose run --rm --entrypoint "\
  openssl req -x509 -nodes -newkey rsa:$rsa_key_size -days 1\
    -keyout '$path/privkey.pem' \
    -out '$path/fullchain.pem' \
    -subj '/CN=localhost'" certbot
echo

# Nginxの起動
echo -e "${GREEN}### Nginxを起動中...${NC}"
docker compose up --force-recreate -d nginx-proxy
echo

# ダミー証明書の削除
echo -e "${GREEN}### ダミー証明書を削除中...${NC}"
docker compose run --rm --entrypoint "\
  rm -Rf /etc/letsencrypt/live/${domains[0]} && \
  rm -Rf /etc/letsencrypt/archive/${domains[0]} && \
  rm -Rf /etc/letsencrypt/renewal/${domains[0]}.conf" certbot
echo

# Let's Encrypt証明書の取得
echo -e "${GREEN}### Let's Encrypt証明書を取得中...${NC}"
domain_args=""
for domain in "${domains[@]}"; do
  domain_args="$domain_args -d $domain"
done

# メールアドレスの設定
case "$email" in
  "") email_arg="--register-unsafely-without-email" ;;
  *) email_arg="--email $email" ;;
esac

# ステージング環境の設定
if [ $staging != "0" ]; then
  staging_arg="--staging"
  echo -e "${YELLOW}注意: テストモードで実行中です。${NC}"
fi

# 証明書の取得
docker compose run --rm --entrypoint "\
  certbot certonly --webroot -w /var/www/certbot \
    $staging_arg \
    $email_arg \
    $domain_args \
    --rsa-key-size $rsa_key_size \
    --agree-tos \
    --force-renewal" certbot
echo

# Nginxの再起動
echo -e "${GREEN}### Nginxを再起動中...${NC}"
docker compose restart nginx-proxy

echo -e "${GREEN}### セットアップ完了!${NC}"
if [ $staging != "0" ]; then
  echo -e "${YELLOW}本番環境に移行するには、staging=0に変更して再実行してください。${NC}"
fi

セットアップ手順(初心者向け詳細版)

ステップ1:プロジェクトのダウンロード

# GitHubからプロジェクトをクローン
git clone https://github.com/superdoccimo/rev.git
cd rev

# または、ファイルを個別に作成する場合
mkdir nginx-letsencrypt
cd nginx-letsencrypt

ステップ2:環境変数ファイルの作成

.envファイルを作成して、環境固有の設定を記述します:

# .envファイルの作成
cat > .env << EOF
# ドメイン設定
DOMAIN=example.com
WWW_DOMAIN=www.example.com

# Let's Encrypt設定
LETSENCRYPT_EMAIL=admin@example.com

# バックエンドサーバー設定
BACKEND_HOST=10.0.0.37
BACKEND_PORT=8080

# 環境設定(staging/production)
ENVIRONMENT=staging
EOF

ステップ3:設定ファイルのカスタマイズ

3.1 ドメイン名の変更

default.confファイルを編集:

# sedコマンドで一括置換(例)
sed -i 's/example.com/あなたのドメイン.com/g' default.conf

# または、テキストエディタで編集
nano default.conf

3.2 バックエンドサーバーの設定

default.confproxy_pass行を編集:

# 例1: ローカルのDockerコンテナの場合
proxy_pass http://app-container:3000;

# 例2: 別のサーバーの場合
proxy_pass http://192.168.1.100:8080;

# 例3: Unix socketの場合
proxy_pass http://unix:/var/run/app.sock;

ステップ4:テストモードでの実行

本番環境で実行する前に、必ずテストモードで動作確認を行います:

# 実行権限を付与
chmod +x init-letsencrypt.sh

# テストモードで実行(staging=1)
sudo ./init-letsencrypt.sh

確認ポイント:

  • ✅ エラーメッセージが表示されていないか
  • ✅ Nginxコンテナが正常に起動しているか
  • ✅ ポート80でアクセスできるか
# コンテナの状態確認
docker compose ps

# ログの確認
docker compose logs nginx-proxy
docker compose logs certbot

ステップ5:動作確認

ブラウザでアクセスして確認:

# HTTPアクセスの確認
curl -I http://あなたのドメイン.com

# HTTPSへのリダイレクト確認(301が返ってくればOK)
# テストモードでは証明書エラーが出るのが正常

ステップ6:本番モードへの切り替え

テストが成功したら、本番証明書を取得します:

# init-letsencrypt.shを編集
nano init-letsencrypt.sh

# staging=1 を staging=0 に変更
staging=0

# 既存の証明書データを削除(重要!)
sudo rm -rf ./data/certbot/conf/*

# 本番モードで実行
sudo ./init-letsencrypt.sh

ステップ7:SSL証明書の確認

# SSL証明書の詳細確認
openssl s_client -connect あなたのドメイン.com:443 -servername あなたのドメイン.com < /dev/null

# 証明書の有効期限確認
docker compose exec nginx-proxy openssl x509 -in /etc/letsencrypt/live/あなたのドメイン.com/cert.pem -text -noout | grep "Not After"

トラブルシューティング(よくある問題と解決方法)

問題1:証明書の取得に失敗する

症状:

Challenge failed for domain example.com

原因と解決方法:

1. DNSの設定ミス

# DNSの確認
dig +short あなたのドメイン.com
# サーバーのIPアドレスが表示されるか確認

# DNSの伝播待ち(最大48時間かかる場合がある)

2. ファイアウォールの設定

# ポート80と443が開いているか確認
sudo ufw status
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

3. 既に別のプロセスがポートを使用

# ポート使用状況の確認
sudo lsof -i :80
sudo lsof -i :443

# 必要に応じて既存のサービスを停止
sudo systemctl stop apache2  # Apacheの場合
sudo systemctl stop nginx    # システムのNginxの場合

問題2:Nginxが起動しない

症状:

nginx-proxy exited with code 1

解決方法:

設定ファイルの文法チェック

# Nginxの設定をテスト
docker compose exec nginx-proxy nginx -t

# エラーが出た場合は、該当行を修正

問題3:証明書の自動更新が機能しない

症状:

証明書の有効期限が近づいても更新されない

解決方法:

手動で更新テスト

# 強制的に更新を実行
docker compose exec certbot certbot renew --dry-run

# 実際に更新
docker compose exec certbot certbot renew --force-renewal

# Nginxを再起動して証明書を反映
docker compose restart nginx-proxy

問題4:HTTPSでアクセスできない

症状:

ERR_SSL_PROTOCOL_ERROR

解決方法:

証明書パスの確認

# 証明書ファイルの存在確認
ls -la ./data/certbot/conf/live/あなたのドメイン/

# 権限の確認と修正
sudo chmod -R 755 ./data/certbot/conf/

問題5:Let’s Encryptのレート制限

症状:

Error creating new order :: too many certificates already issued

解決方法:

  • 同一ドメインでの証明書発行は週5回まで
  • テストはstaging環境を使用する
  • 1週間待ってから再試行

カスタマイズ方法

複数サイトの運用

リバースプロキシの大きな利点は、複数のWebサイトを一元管理できることです。

設定例1:サブドメインでの振り分け

# blog.example.com用の設定
server {
    listen 443 ssl http2;
    server_name blog.example.com;
    
    ssl_certificate /etc/letsencrypt/live/blog.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/blog.example.com/privkey.pem;
    
    location / {
        proxy_pass http://localhost:8080;  # WordPressコンテナ
    }
}

# portfolio.example.com用の設定
server {
    listen 443 ssl http2;
    server_name portfolio.example.com;
    
    ssl_certificate /etc/letsencrypt/live/portfolio.example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/portfolio.example.com/privkey.pem;
    
    location / {
        proxy_pass http://localhost:8888;  # ポートフォリオサイト
    }
}

設定例2:パスベースでの振り分け

server {
    listen 443 ssl http2;
    server_name example.com;
    
    # ブログ用
    location /blog {
        proxy_pass http://localhost:8080/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
    
    # API用
    location /api {
        proxy_pass http://localhost:3000/;
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
    }
    
    # 静的ファイル
    location /static {
        alias /var/www/static;
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}
パスベース振り分け図

パスベース振り分け方式

単一ドメインでパスによる柔軟なルーティング制御

🌐 単一ドメイン
example.com
1つのドメインですべてのサービスを統合管理
example.com/
メインサイト・ランディングページ
example.com/blog
ブログ・記事コンテンツ
example.com/api
REST API・データ処理
example.com/static
静的ファイル・アセット
🔀 リバースプロキシ
URLパスに基づいて適切なバックエンドサーバーに自動振り分け
📋 ルーティングテーブル
location /blog
proxy_pass http://localhost:8080/
location /api
proxy_pass http://localhost:3000/
location /static
alias /var/www/static
location /
proxy_pass http://localhost:8888/
メインサイト
React / Next.js
ポート: 8888
パス: /
  • ランディングページ
  • 企業情報
  • サービス紹介
  • お問い合わせ
ブログシステム
WordPress
ポート: 8080
パス: /blog
  • 記事の投稿・管理
  • カテゴリ・タグ機能
  • コメント・SNS連携
  • SEO最適化
APIサーバー
Node.js / Express
ポート: 3000
パス: /api
  • RESTful API
  • データベース連携
  • 認証・認可
  • 外部API統合
静的ファイル
Nginx Direct
パス: /static
ストレージ: /var/www/static
  • 画像・動画ファイル
  • CSS・JSファイル
  • PDFドキュメント
  • キャッシュ最適化
🔍 パス方式 vs サブドメイン方式 比較
パスベース方式
メリット
  • 1つのSSL証明書で全体をカバー
  • ドメイン管理がシンプル
  • SEOでドメインオーソリティが統合
  • 設定変更が容易
デメリット
  • URL構造の制約
  • 各サービスの独立性が低い
  • パスの衝突リスク
  • ログ分析の複雑さ
サブドメイン方式
メリット
  • 各サービスの完全な独立性
  • 個別SSL証明書によるセキュリティ
  • ブランディング効果
  • スケーラビリティの高さ
デメリット
  • DNS設定の複雑さ
  • SSL証明書の個別管理
  • ドメインオーソリティの分散
  • 設定ファイルの増加

パフォーマンスチューニング

1. キャッシュの設定

# プロキシキャッシュの設定
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=1g inactive=60m use_temp_path=off;

server {
    location / {
        proxy_cache my_cache;
        proxy_cache_valid 200 60m;
        proxy_cache_valid 404 1m;
        proxy_cache_bypass $http_pragma $http_authorization;
        
        add_header X-Cache-Status $upstream_cache_status;
        proxy_pass http://backend;
    }
}

2. 圧縮の最適化

# Brotli圧縮の追加(より高い圧縮率)
load_module modules/ngx_http_brotli_module.so;

http {
    # Brotli設定
    brotli on;
    brotli_comp_level 6;
    brotli_types text/plain text/css text/xml text/javascript application/json application/javascript application/xml+rss application/rss+xml application/atom+xml image/svg+xml;
}

セキュリティの強化

1. レート制限の実装

# DDoS対策用のレート制限
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;

server {
    location /api {
        limit_req zone=mylimit burst=20 nodelay;
        proxy_pass http://backend;
    }
}

2. IPアドレス制限

# 管理画面へのアクセス制限
location /admin {
    allow 192.168.1.0/24;  # 社内ネットワーク
    allow 203.0.113.5;     # 管理者の固定IP
    deny all;
    
    proxy_pass http://backend;
}

Docker Composeの高度な設定

1. ヘルスチェックの追加

services:
  nginx-proxy:
    image: nginx:alpine
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost/health"]
      interval: 30s
      timeout: 10s
      retries: 3
      start_period: 40s

2. リソース制限

services:
  nginx-proxy:
    image: nginx:alpine
    deploy:
      resources:
        limits:
          cpus: '0.5'
          memory: 512M
        reservations:
          cpus: '0.25'
          memory: 256M

自動化とメンテナンス

証明書の自動更新設定

Let’s Encryptの証明書は90日で期限切れになるため、自動更新が重要です。

systemdタイマーを使用した方法(推奨)

# 1. systemdサービスファイルの作成
sudo cat > /etc/systemd/system/certbot-renewal.service << EOF
[Unit]
Description=Certbot Renewal
After=docker.service
Requires=docker.service

[Service]
Type=oneshot
WorkingDirectory=/path/to/your/project
ExecStart=/usr/bin/docker compose exec -T certbot certbot renew --quiet
ExecStartPost=/usr/bin/docker compose exec -T nginx-proxy nginx -s reload

[Install]
WantedBy=multi-user.target
EOF

# 2. タイマーファイルの作成
sudo cat > /etc/systemd/system/certbot-renewal.timer << EOF
[Unit]
Description=Run Certbot Renewal twice daily

[Timer]
OnCalendar=*-*-* 00,12:00:00
RandomizedDelaySec=3600
Persistent=true

[Install]
WantedBy=timers.target
EOF

# 3. サービスの有効化と開始
sudo systemctl daemon-reload
sudo systemctl enable certbot-renewal.timer
sudo systemctl start certbot-renewal.timer

# 4. 状態確認
sudo systemctl status certbot-renewal.timer
sudo systemctl list-timers

ログ管理

ログローテーションの設定

# /etc/logrotate.d/nginx-docker を作成
sudo cat > /etc/logrotate.d/nginx-docker << EOF
/var/lib/docker/containers/*/*.log {
    daily
    rotate 7
    compress
    delaycompress
    missingok
    notifempty
    create 0640 root root
    sharedscripts
    postrotate
        docker compose exec -T nginx-proxy nginx -s reopen
    endscript
}
EOF

監視設定

1. 証明書の有効期限監視スクリプト

#!/bin/bash
# check-cert-expiry.sh

DOMAIN="example.com"
DAYS_WARNING=30

expiry_date=$(docker compose exec -T nginx-proxy \
    openssl x509 -in /etc/letsencrypt/live/$DOMAIN/cert.pem -noout -enddate \
    | cut -d= -f2)

expiry_epoch=$(date -d "$expiry_date" +%s)
current_epoch=$(date +%s)
days_left=$(( ($expiry_epoch - $current_epoch) / 86400 ))

if [ $days_left -lt $DAYS_WARNING ]; then
    echo "警告: SSL証明書の有効期限まで残り${days_left}日です!"
    # メール通知やSlack通知を送信
fi

2. Docker Composeでのヘルスチェック統合

services:
  monitoring:
    image: prom/prometheus:latest
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
    ports:
      - "9090:9090"
    networks:
      - webproxy

ベストプラクティスとセキュリティ推奨事項

2025年最新のセキュリティ推奨設定

1. TLS設定の最適化

# 最新の推奨TLS設定
ssl_protocols TLSv1.2 TLSv1.3;
ssl_ciphers 'TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384';
ssl_prefer_server_ciphers off;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 1d;
ssl_session_tickets off;

2. セキュリティヘッダーの完全版

# セキュリティヘッダー
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
add_header Content-Security-Policy "default-src 'self' https:; script-src 'self' 'unsafe-inline' 'unsafe-eval' https:; style-src 'self' 'unsafe-inline' https:;" always;
add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

パフォーマンス最適化

1. HTTP/2 と HTTP/3 の設定

# HTTP/2の有効化(既にデフォルトで含まれている)
listen 443 ssl http2;

# HTTP/3(QUIC)の有効化(Nginx 1.25.0以降)
listen 443 quic reuseport;
listen 443 ssl http2;
add_header Alt-Svc 'h3=":443"; ma=86400';

2. 接続の最適化

# Keep-alive接続の最適化
keepalive_timeout 65;
keepalive_requests 100;

# バッファサイズの最適化
client_body_buffer_size 128k;
client_max_body_size 100m;
client_header_buffer_size 1k;
large_client_header_buffers 4 4k;
output_buffers 1 32k;
postpone_output 1460;

監視とアラート

Prometheus + Grafanaでの監視

# docker-compose.yml に追加
services:
  nginx-exporter:
    image: nginx/nginx-prometheus-exporter:latest
    ports:
      - "9113:9113"
    command:
      - -nginx.scrape-uri=http://nginx-proxy/nginx_status
    networks:
      - webproxy
    depends_on:
      - nginx-proxy

よくある質問(FAQ)

Q1: 無料のLet’s Encrypt証明書で十分ですか?

A: はい、ほとんどのケースで十分です。Let’s Encryptは:

  • 主要なブラウザすべてで信頼されています
  • 自動更新により管理が簡単です
  • DV(ドメイン認証)証明書として機能します

ただし、以下の場合は有料証明書を検討してください:

  • EV(拡張認証)証明書が必要な場合
  • ワイルドカード証明書で多数のサブドメインを管理する場合
  • 法的な補償が必要な場合

Q2: 証明書の更新に失敗した場合はどうなりますか?

A: 自動更新は期限の30日前から試行されるため、複数回の再試行機会があります。手動での対処方法:

# 更新状態の確認
docker compose exec certbot certbot certificates

# 手動更新
docker compose exec certbot certbot renew --force-renewal

# Nginxの再起動
docker compose restart nginx-proxy

Q3: 複数ドメインで1つの証明書を使えますか?

A: はい、SAN(Subject Alternative Names)証明書として可能です:

# init-letsencrypt.shで複数ドメインを指定
domains=(example.com www.example.com api.example.com)

Q4: バックアップはどうすればいいですか?

A: 以下のディレクトリをバックアップしてください:

# バックアップスクリプトの例
#!/bin/bash
BACKUP_DIR="/backup/nginx-letsencrypt"
DATE=$(date +%Y%m%d_%H%M%S)

# 証明書のバックアップ
tar -czf $BACKUP_DIR/certbot_$DATE.tar.gz ./data/certbot/

# 設定ファイルのバックアップ
tar -czf $BACKUP_DIR/config_$DATE.tar.gz *.conf docker-compose.yml

# 古いバックアップの削除(30日以上)
find $BACKUP_DIR -name "*.tar.gz" -mtime +30 -delete

Q5: パフォーマンスが遅い場合の対処法は?

A: 以下の点を確認してください:

  1. リソース不足の確認
docker stats
htop
  1. Nginxのワーカープロセス数を調整
worker_processes auto;  # CPUコア数に合わせて自動調整
  1. キャッシュの有効化
  2. 不要なログの削減

まとめ

このガイドでは、2025年最新のベストプラクティスに基づいて、Docker + Nginx + Let’s Encryptを使用した自動SSL更新リバースプロキシの構築方法を詳しく説明しました。

達成できたこと

セキュアな環境の構築

  • TLS 1.2/1.3による暗号化通信
  • 自動証明書更新による継続的なセキュリティ
  • 最新のセキュリティヘッダーの実装

運用の自動化

  • Docker Compose V2による簡単な管理
  • Let’s Encryptの自動更新
  • ログローテーションの自動化

スケーラブルな構成

  • 複数サイトの一元管理
  • 負荷分散の実装可能性
  • マイクロサービスアーキテクチャへの対応

次のステップ

このシステムをさらに発展させるために:

  1. 監視システムの追加(Prometheus + Grafana)
  2. CI/CDパイプラインの構築(GitHub Actions等)
  3. Kubernetes環境への移行(より大規模な運用向け)
  4. WAF(Web Application Firewall)の導入(ModSecurity等)

関連リソース

関連記事

リバースプロキシ関連の記事:

チュートリアル動画:

最終更新日: 2025年9月
バージョン: 2.0
著者: mamu minokamo

この記事が役に立った場合は、GitHubでスターをいただけると幸いです! 質問や改善提案がある場合は、GitHubのIssueでお知らせください。

タイトルとURLをコピーしました