shintaro.info infra

Apache (Webサーバ)

作成: Shintaro / Acia
対象OS: Debian 13 (Sakura VPS)


1. 概要

  • 目的: サーバ上の Apache を、*:80 の来訪を apex → https へ誘導し、https://example.com は当面空(非公開)とし、https://www.example.com で配信する構成に統一する。
  • 証明書: Let’s Encrypt(webroot方式)で SANexample.com / www.example.com / mail.example.com を同居させた 1枚 を利用。
  • ACME: すべての vhost で ACME チャレンジ(/.well-known/acme-challenge/)への到達を 例外的に許可
  • 自動更新: certbot timer/cron と deploy hook(堅牢版) で Apache / Postfix / Dovecot を安全に再読込。
  • 本章は 証明書取得「前」取得「後」 の作業を明確に分離する。
  • Mermaid 図は削除・省略・改変せず保持する(VSCode互換のラベルに調整)。

2. 前提 / DNS / Firewall

  • UFW: Apache (80/tcp) / Apache Secure (443/tcp) を許可済
  • さくらVPS: パケットフィルタで 80/443 を許可
  • DNS 要件: 下表のとおり VPS グローバルIPへ向ける
FQDNレコード
example.comAVPSグローバルIP
www.example.comA同上
mail.example.comA同上(将来の Postfix/Dovecot も同サーバでTLS使用)

3. Apache のインストール

sudo apt update
sudo apt install -y apache2
sudo a2enmod ssl rewrite
sudo systemctl enable apache2
sudo systemctl start apache2

4. 事前ステップ(証明書取得の準備:HTTP構成)

4.1 ドキュメントルート / ACME webroot の作成

sudo mkdir -p /var/www/www.example.com
echo '<h1>Hello from www.example.com</h1>' | sudo tee /var/www/www.example.com/index.html >/dev/null
sudo mkdir -p /var/www/letsencrypt/.well-known/acme-challenge
sudo chown -R root:root /var/www
sudo chmod -R 755 /var/www

4.2 ACME スニペット(全 vhost 共通で include)

ファイル: /etc/apache2/snippets/acme.conf

%% HTTP段階 ACME 例外許可(簡素ラベル。VSCode互換)
flowchart LR
  C[Client] -->|HTTP 80| Any80["*:80 (HTTP vhosts)"]
  Any80 -->|ACME challenge| Webroot[ACME webroot]
  Any80 -->|301| Apex["example.com:80"]
  Any80 -->|301| WWW["www.example.com:80"]
  classDef http fill:#fde68a,stroke:#111111,color:#111111,stroke-width:2px
  class Any80,Apex,WWW http
Alias /.well-known/acme-challenge/ /var/www/letsencrypt/.well-known/acme-challenge/
<Directory "/var/www/letsencrypt/.well-known/acme-challenge/">
    Options None
    AllowOverride None
    Require all granted
</Directory>

4.3 HTTP VirtualHost(:80

  • ファイル: /etc/apache2/sites-available/000-default.conf(受け皿 → apex http へ)
    <VirtualHost *:80>
        Include /etc/apache2/snippets/acme.conf
        RewriteEngine On
        RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/
        RewriteRule ^ http://example.com%{REQUEST_URI} [R=301,L]
    </VirtualHost>
  • ファイル: /etc/apache2/sites-available/example.com.conf(apex→httpsへ誘導、ACMEは例外許可)
    <VirtualHost *:80>
        ServerName example.com
        Include /etc/apache2/snippets/acme.conf
        RewriteEngine On
        RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/
        RewriteRule ^ https://example.com%{REQUEST_URI} [R=301,L]
    </VirtualHost>
  • ファイル: /etc/apache2/sites-available/www.example.com.conf(www→httpsへ誘導、ACMEは例外許可)
    <VirtualHost *:80>
        ServerName www.example.com
        Include /etc/apache2/snippets/acme.conf
        RewriteEngine On
        RewriteCond %{REQUEST_URI} !^/\.well-known/acme-challenge/
        RewriteRule ^ https://www.example.com%{REQUEST_URI} [R=301,L]
    </VirtualHost>

4.4 サイト有効化と Apache reload(HTTP段階)

sudo a2ensite 000-default.conf example.com.conf www.example.com.conf
sudo systemctl reload apache2

5. 証明書の取得(Let’s Encrypt / webroot)

5.1 certbot インストールと発行

sudo apt install -y certbot
sudo certbot certonly --webroot       -w /var/www/letsencrypt       --cert-name example.com       -d example.com -d www.example.com -d mail.example.com

5.2 証明書の確認

sudo certbot certificates
openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -noout -text | grep -E 'Subject:|DNS:'
%% 証明書発行フロー(簡素ラベル)
sequenceDiagram
  participant Certbot
  participant DNS as DNS (domain → VPS)
  participant Apache
  participant ACME as Let's Encrypt

  Certbot->>DNS: SAN = { apex, www, mail }
  Certbot->>Apache: webroot = /var/www/letsencrypt
  ACME-->>Apache: HTTP-01
  Apache-->>ACME: 200 (allow ACME challenge)
  ACME-->>Certbot: issue certificate (SANs on one cert)
  Certbot->>Apache: deploy hook reload

補足: vHost を新規追加・名称変更した場合は、再度 sudo a2ensite <file>.conf && sudo systemctl reload apache2 を実行して反映する。


6. 事後ステップ(証明書取得後:HTTPS構成)

6.1 example.com-ssl.conf(apex https → wwwへ恒久リダイレクト)

ファイル: /etc/apache2/sites-available/example.com-ssl.conf

    <IfModule mod_ssl.c>
    <VirtualHost *:443>
        ServerName example.com
        SSLEngine on
        SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
        Redirect permanent / https://www.example.com/
    </VirtualHost>
    </IfModule>

6.2 www.example.com-ssl.conf(配信本体)

ファイル: /etc/apache2/sites-available/www.example.com-ssl.conf

    <IfModule mod_ssl.c>
    <VirtualHost *:443>
        ServerName www.example.com
        DocumentRoot /var/www/www.example.com
        SSLEngine on
        SSLCertificateFile /etc/letsencrypt/live/example.com/fullchain.pem
        SSLCertificateKeyFile /etc/letsencrypt/live/example.com/privkey.pem
        <Directory /var/www/www.example.com>
            Options -Indexes
            AllowOverride None
            Require all granted
        </Directory>
    </VirtualHost>
    </IfModule>

6.3 サイト有効化と Apache reload(HTTPS段階)

sudo a2ensite example.com-ssl.conf www.example.com-ssl.conf
sudo systemctl reload apache2

6.4 最終構成図(HTTP/HTTPS ルーティング)

%%{init: {"theme":"base","themeVariables":{"background":"transparent","fontFamily":"system-ui, sans-serif","primaryTextColor":"#111827","lineColor":"#2563eb","clusterBkg":"#f8fafc","clusterBorder":"#64748b","edgeLabelBackground":"#ffffff"}}}%%
%% 最終状態ルーティング(簡素ラベル)
flowchart LR
  C[Client] -->|HTTP 80| D80["000-default :80"]
  C -->|HTTP 80| A80["example.com :80"]
  C -->|HTTP 80| W80["www.example.com :80"]
  C -->|HTTPS 443| A443["example.com :443"]
  C -->|HTTPS 443| W443["www.example.com :443"]

  subgraph HTTP80["HTTP (80)"]
    D80 -->|301| A80
    A80 -->|301| A443
    W80 -->|301| W443
    class D80,A80,W80 http
  end

  subgraph HTTPS443["HTTPS (443)"]
    A443 -->|301| W443
    W443 -->|200| Site((/var/www/www.example.com))
    class A443,W443 https
  end

  C -.->|ACME challenge| D80
  C -.->|ACME challenge| A80
  C -.->|ACME challenge| W80

  class C client
  classDef client fill:#dbeafe,stroke:#1d4ed8,color:#111827,stroke-width:2px
  classDef http fill:#fef3c7,stroke:#b45309,color:#111827,stroke-width:2px
  classDef https fill:#dcfce7,stroke:#15803d,color:#111827,stroke-width:2px
  style HTTP80 fill:#fff7ed,stroke:#c2410c,color:#111827,stroke-width:2px
  style HTTPS443 fill:#f0fdf4,stroke:#15803d,color:#111827,stroke-width:2px
  style Site fill:#ffffff,stroke:#475569,color:#111827,stroke-width:2px
  linkStyle default stroke:#2563eb,stroke-width:2px,color:#111827

7. 自動更新と deploy hook(堅牢版)

7.1 timer/cron の確認と dry-run

systemctl list-timers | grep certbot || cat /etc/cron.d/certbot
sudo certbot renew --dry-run

7.2 deploy hook(Apache/Postfix/Dovecot を安全に reload)

ファイル: /etc/letsencrypt/renewal-hooks/deploy/000-reload-services.sh

#!/bin/bash
# certbot による証明書更新後に呼ばれるフック
# 目的:Apache / Postfix / Dovecot を安全にリロード(無ければスキップ)
set -e
log() { logger -t certbot-deploy "$*"; }
services=(apache2 postfix dovecot)
for svc in "${services[@]}"; do
  if systemctl list-unit-files --type=service | awk '{print $1}' | grep -qx "${svc}.service"; then
    if systemctl reload "${svc}" 2>/dev/null; then
      log "reloaded ${svc}"
    elif systemctl try-reload-or-restart "${svc}" 2>/dev/null; then
      log "try-reload-or-restart ${svc}"
    else
      log "WARN: failed to reload/restart ${svc} (continuing)"
      true
    fi
  else
    log "skip ${svc}: service not installed"
  fi
done
chmod +x /etc/letsencrypt/renewal-hooks/deploy/000-reload-services.sh

8. 検証コマンド(例)

# 80系
curl -I http://<IP>/
curl -I http://example.com/
curl -I http://www.example.com/

# 443系(証明書検証をスキップするなら -k)
curl -I -k https://example.com/
curl -I -k https://www.example.com/

# ACME到達性
curl -I http://example.com/.well-known/acme-challenge/test
curl -I http://www.example.com/.well-known/acme-challenge/test
curl -I http://mail.example.com/.well-known/acme-challenge/test

9. 運用・注意点

  • 80番//.well-known/acme-challenge/ の例外許可を 常に維持(HTTP-01更新のため)
  • SANの増減がある場合は、再発行時に“全SAN列挙” を忘れない
  • /etc/letsencrypt/live/example.com/Apache/メールで共用(鍵の権限: Postfix/Dovecot 側で ssl-cert グループを活用)
  • バックアップ: /etc/apache2/, /etc/letsencrypt/, /var/www/ を定期同期

10. 将来拡張(メールサーバ)

  • mail.example.com を同一証明書でTLS化(Postfix/Dovecot)
  • 鍵読み取り権限:
    sudo usermod -aG ssl-cert postfix
    sudo usermod -aG ssl-cert dovecot
  • deploy hook は本書の堅牢版を流用(すでに導入済)

変更履歴

日付編集者内容
2025-09-30Shintaro / Acia初版作成(Apache SSL統合構成:SAN, webroot, deploy hook)。
2025-10-28Acia章構成を「証明書取得前/後」で分離。a2ensite とディレクトリ作成手順を追記。Mermaid 図を維持。
2025-10-28AciavHost設定ファイルのフルパスを各セクションに明記し、再現性を向上。