SSL/TLS証明書とは

SSL/TLS証明書は、Webサイトとユーザーのブラウザ間の通信を暗号化するために使われます。

HTTPSのメリット

  • 🔒 通信の暗号化: データの盗聴を防ぐ
  • 信頼性の証明: サイトの正当性を保証
  • 🚀 SEOの向上: Googleが推奨(ランキング要因)
  • 📱 PWA対応: Service Workerに必須
  • 🌐 HTTP/2利用: 高速な通信プロトコル

従来のSSL証明書の問題

  • 💰 年間数千円~数万円のコスト
  • 📝 複雑な申請手続き
  • 手動での更新作業

Let's Encryptはこれらの問題をすべて解決します!

Let's Encryptとは

Let's Encryptは、無料で自動化されたSSL証明書を提供する認証局(CA)です。

特徴

  • 完全無料
  • 自動更新可能
  • ✅ 主要ブラウザで信頼されている
  • ✅ APIで自動化できる
  • ⚠️ 有効期限は90日(自動更新で解決)

certbotツール

certbotは、Let's Encrypt証明書の取得・更新を自動化するツールです。

前提条件の確認

必要なもの

  • ✅ 独自ドメイン(example.comなど)
  • ✅ ドメインがサーバーのIPに紐付いている(Aレコード設定済み)
  • ✅ ポート80と443が開いている
  • ✅ Nginxがインストール済み

DNS設定の確認

# ドメインの名前解決ができるか確認
dig +short example.com
# → 123.45.67.89(サーバーのIP)が返ればOK

# または
nslookup example.com

Nginxのインストールと設定

Nginxのインストール

# Ubuntuの場合
sudo apt update
sudo apt install -y nginx

# 起動と自動起動設定
sudo systemctl start nginx
sudo systemctl enable nginx

# 確認
sudo systemctl status nginx

初期設定ファイルの作成

まずHTTPで動作させる設定を作成します:

# /etc/nginx/sites-available/myapp の作成
sudo nano /etc/nginx/sites-available/myapp

以下の内容を記述:

server {
    listen 80;
    server_name example.com www.example.com;

    location / {
        proxy_pass http://localhost:8000;
        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;
    }
}

設定の有効化

# シンボリックリンクの作成
sudo ln -s /etc/nginx/sites-available/myapp \
  /etc/nginx/sites-enabled/

# デフォルト設定の削除
sudo rm /etc/nginx/sites-enabled/default

# 設定の検証
sudo nginx -t

# Nginxの再起動
sudo systemctl reload nginx

certbotのインストール

Ubuntu/Debianの場合

# Snapでインストール(推奨)
sudo snap install --classic certbot

# シンボリックリンクの作成
sudo ln -s /snap/bin/certbot /usr/bin/certbot

# 確認
certbot --version

apt経由でのインストール(代替方法)

sudo apt update
sudo apt install -y certbot python3-certbot-nginx

SSL証明書の取得

自動設定モード(推奨)

Nginxの設定を自動で書き換えてくれます

sudo certbot --nginx -d example.com -d www.example.com

インタラクティブな質問に回答

# メールアドレスの入力
Enter email address: your_email@example.com

# 利用規約への同意
(A)gree/(C)ancel: A

# メール配信の設定
(Y)es/(N)o: N  # 任意

# HTTPSリダイレクトの設定
1: No redirect
2: Redirect - Make all requests redirect to secure HTTPS
Select: 2  # 推奨: すべてHTTPSにリダイレクト

手動設定モード

証明書のみ取得し、Nginx設定は手動で行う場合:

sudo certbot certonly --nginx -d example.com -d www.example.com

証明書ファイルの場所

/etc/letsencrypt/live/example.com/
├── fullchain.pem   # 証明書チェーン(Nginxで使用)
├── privkey.pem     # 秘密鍵(Nginxで使用)
├── cert.pem        # サーバー証明書
└── chain.pem       # 中間証明書

Nginx設定の手動カスタマイズ

自動設定でうまくいかない場合や、細かくカスタマイズしたい場合:

server {
    listen 80;
    server_name example.com www.example.com;
    
    # HTTPからHTTPSへリダイレクト
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    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設定の最適化
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256';
    ssl_prefer_server_ciphers off;

    # HSTSの有効化(推奨)
    add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" 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;

    location / {
        proxy_pass http://localhost:8000;
        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;
    }
}

設定後:

sudo nginx -t
sudo systemctl reload nginx

Docker Composeとの統合

docker-compose.ymlの例

version: '3.8'

services:
  web:
    build: .
    container_name: flask_app
    restart: always
    expose:
      - "8000"
    volumes:
      - ./:/app
    command: gunicorn -w 4 -b 0.0.0.0:8000 app:app

  nginx:
    image: nginx:alpine
    container_name: nginx_proxy
    restart: always
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - ./nginx/nginx.conf:/etc/nginx/conf.d/default.conf
      - /etc/letsencrypt:/etc/letsencrypt:ro
      - /var/lib/letsencrypt:/var/lib/letsencrypt:ro
    depends_on:
      - web

Nginxコンテナ用の設定ファイル

nginx/nginx.conf:

upstream flask_app {
    server web:8000;
}

server {
    listen 80;
    server_name example.com www.example.com;
    return 301 https://$server_name$request_uri;
}

server {
    listen 443 ssl http2;
    server_name example.com www.example.com;

    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;

    location / {
        proxy_pass http://flask_app;
        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;
    }
}

自動更新の設定

更新テストの実行

# Dry-run(実際には更新しない)
sudo certbot renew --dry-run

cronでの自動更新

certbotはsystemdタイマーで自動的に更新を試行します:

# タイマーの確認
sudo systemctl list-timers | grep certbot

# 手動でタイマーを有効化(通常は不要)
sudo systemctl enable certbot.timer
sudo systemctl start certbot.timer

更新後のNginxリロード

更新時に自動でNginxをリロードする設定:

# /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh を作成
sudo nano /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

内容:

#!/bin/bash
sudo systemctl reload nginx

実行権限を付与:

sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh

HTTPSの確認

ブラウザでの確認

  • 🔒 鍵マークが表示される
  • 証明書情報でLet's Encryptが発行者と確認できる
  • 有効期限を確認(90日以内)

SSL Labsでのテスト

SSL/TLSの設定品質をチェックできます:

https://www.ssllabs.com/ssltest/analyze.html?d=example.com

目標: A+評価

コマンドラインでの確認

# 証明書の詳細表示
openssl s_client -connect example.com:443 -servername example.com

# 有効期限の確認
echo | openssl s_client -connect example.com:443 2>/dev/null | \
  openssl x509 -noout -dates

トラブルシューティング

証明書取得エラー: "DNS問題"

  • ドメインのDNS設定を確認
  • DNSの反映を待つ(最大48時間)
  • ファイアウォールでポート80/443が開いているか確認
sudo ufw status
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp

証明書取得エラー: "Rate limit"

Let's Encryptにはレート制限があります:

  • 同一ドメイン: 週50回まで
  • 失敗試行: 時間あたり5回まで

解決策:

  • テスト時は--dry-run--stagingオプションを使う
  • 時間をおいて再試行

"Permission denied" エラー

# certbot実行時はsudoを使う
sudo certbot ...

# 証明書ファイルの権限確認
sudo ls -la /etc/letsencrypt/live/example.com/

Nginxが証明書を読み込めない

# Nginxの設定テスト
sudo nginx -t

# エラーログの確認
sudo tail -f /var/log/nginx/error.log

# 証明書パスの確認
sudo ls -l /etc/letsencrypt/live/example.com/fullchain.pem

更新が自動実行されない

# タイマーの状態確認
sudo systemctl status certbot.timer

# ログの確認
sudo journalctl -u certbot.timer

# 手動での更新テスト
sudo certbot renew --dry-run

セキュリティのベストプラクティス

1. 強力なSSL設定

  • TLS 1.2以上のみを許可
  • 弱い暗号スイートを無効化
  • HSTS(HTTP Strict Transport Security)の有効化

2. セキュリティヘッダーの追加

add_header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "no-referrer-when-downgrade" always;
add_header Content-Security-Policy "default-src 'self' http: https: data: blob: 'unsafe-inline'" always;

3. 定期的な監視

  • 証明書の有効期限を監視
  • 自動更新のログを定期確認
  • SSL Labsで定期的にテスト

料金とコスト

  • Let's Encrypt: 完全無料
  • 更新: 無料(自動)
  • ワイルドカード証明書: 無料(DNS-01チャレンジが必要)

年間の追加コストはゼロです!

高度な設定

ワイルドカード証明書の取得

*.example.comのような証明書:

sudo certbot certonly --manual \
  --preferred-challenges=dns \
  -d example.com -d '*.example.com'

DNS TXTレコードの追加が必要になります。

複数ドメインの証明書

sudo certbot --nginx \
  -d example.com \
  -d www.example.com \
  -d api.example.com \
  -d blog.example.com

まとめ

この記事では、Let's Encryptで無料HTTPS化する方法を解説しました。

学んだこと

  • Let's Encryptの仕組みと利点
  • certbotでのSSL証明書取得
  • NginxでのHTTPS設定
  • 自動更新の仕組み
  • Docker環境での統合
  • セキュリティのベストプラクティス

これでWebサービスが完成!

Flask + Gunicorn + Docker + Nginx + HTTPSの構成で、本番環境レベルのWebサービスが構築できました。

次のステップ

  • CDN(CloudflareなどでのDDoS対策
  • モニタリング(Prometheus, Grafana)
  • バックアップの自動化
  • CI/CDパイプラインの構築