【2026年1月最新】SSL証明書無料取得完全ガイド|Let’s Encrypt自動更新・acme.sh・ワイルドカード対応

※本記事は2026年1月時点の情報に基づいて更新しています。Let’s Encrypt は現状「90日証明書(classic)」が基本ですが、将来的に有効期間短縮が段階的に進む予定です。運用では「手動更新」や「更新間隔の固定」ではなく、自動更新(定期実行)+期限監視を前提に設計してください。

Webサイトの常時SSL化は、もはや必須の要件となっています。Let’s Encrypt を活用すれば、無料でSSL/TLS証明書を取得・運用できます。ただし Let’s Encrypt の証明書は短い有効期間で運用されるため、証明書の自動更新ができていないと、期限切れでサービス停止につながります。特にゲームサーバーの管理パネル、API、管理画面など「止められない」サービスでは、更新の自動化と監視は必須です。

参考として、日本語の解説動画(YouTube)も埋め込んでいます。必要に応じて併せて確認してください。

目次

SSL証明書とLet’s Encryptの基礎

SSL/TLS証明書の役割

SSL/TLS証明書は、Webサイトとユーザー間の通信を暗号化し、さらに「そのサイトが本物である」こと(ドメインの管理者であること)を証明するための仕組みです。HTTPS 化により、以下の効果が得られます。

  • 盗聴・改ざんの防止:通信内容(ログイン情報、APIキー、Cookieなど)を保護
  • なりすまし防止:偽サイトへの誘導リスクを低減
  • ブラウザ警告の回避:非HTTPSは「保護されていない通信」と表示されやすい

Let’s Encryptの特徴

  • 完全無料:費用負担なしで証明書取得
  • 自動化対応:ACMEプロトコルにより自動取得・自動更新が可能
  • 主要ブラウザで信頼済み:一般的なWeb利用で警告が出にくい
  • 短い有効期間:90日(今後短縮予定)なので自動化が前提

acme.shによる証明書管理

acme.sh は、シンプルで扱いやすい ACME クライアントです。Let’s Encrypt の証明書取得・更新を自動化でき、DNS-01(ワイルドカード対応)にも強いのが特徴です。以降は「取得 → 配置 → 自動更新」の流れで、運用に耐える形を作ります。

acme.shの基本セットアップ

インストール方法は複数ありますが、まずは公式の導入手順に近い形で入れるのが安全です。サーバーに合わせて、git か curl のどちらかを選んでください。

# acme.sh のインストール(git)
git clone https://github.com/acmesh-official/acme.sh.git
cd acme.sh
sudo ./acme.sh --install --accountemail your-email@example.com

# または curl(1行)
curl https://get.acme.sh | sh -s email=your-email@example.com

環境変数の設定

acme.sh を PATH に通しておくと、cron や systemd で呼び出すときに事故が減ります。利用ユーザー(root運用 or 専用ユーザー運用)に合わせて PATH を設定してください。

# 例:.bashrc または .profile に追加
export PATH="$HOME/.acme.sh:$PATH"
source ~/.bashrc

# 動作確認
acme.sh --version

基本的な証明書取得

主な認証方式は3つです。運用上のおすすめは、停止不要でワイルドカードにも対応できる DNS-01 です。

  • Webroot(http-01):Web公開ディレクトリに検証ファイルを書き込む。既存Webが動いている環境向け。
  • Standalone:acme.sh が一時的に 80/TCP を使う。Webサーバー停止が必要になりやすい。
  • DNS-01(推奨):DNS に TXT を自動追加。ワイルドカード対応、停止不要。
# Webroot認証(http-01)
acme.sh --issue -d example.com -w /var/www/html

# Standalone認証(Webサーバー停止が必要になることが多い)
acme.sh --issue -d example.com --standalone

# DNS認証(推奨:ワイルドカードも可能)
# ※例として Cloudflare の dns_cf を使用
acme.sh --issue -d example.com --dns dns_cf

DNS認証による自動化

DNS-01 は「DNS に TXT レコードを追加できる権限」が必要です。acme.sh は多くの DNS プロバイダーに対応しており、APIキーを環境変数で渡すと自動で TXT を更新します。重要なのは、APIキーは最小権限にし、漏えい対策(権限分離、秘匿、ローテーション)を前提にすることです。

Cloudflare DNS の設定

Cloudflare は利用者が多く、DNS-01 自動化の代表例です。Cloudflare 側の API トークン/キーを用意し、acme.sh が参照できる形で設定します。

# Cloudflare API設定(例:従来形式)
export CF_Key="your-cloudflare-api-key"
export CF_Email="your-email@example.com"

# ワイルドカード込みで発行(DNS-01)
acme.sh --issue -d example.com -d "*.example.com" --dns dns_cf

# 設定の永続化(証明書配置+リロード)
acme.sh --install-cert -d example.com \
  --key-file /etc/ssl/private/example.com.key \
  --fullchain-file /etc/ssl/certs/example.com.pem \
  --reloadcmd "systemctl reload nginx"

Route53 DNS の設定

AWS Route53 を使う場合は、IAM のアクセスキーを用意します。ここでも最小権限(対象 Hosted Zone の変更権限のみ)を推奨します。

# AWS認証情報設定
export AWS_ACCESS_KEY_ID="AKIAxxxxxxxxxxxxxxxx"
export AWS_SECRET_ACCESS_KEY="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

# Route53でワイルドカード証明書取得
acme.sh --issue -d example.com -d "*.example.com" --dns dns_aws

その他DNSプロバイダーの対応

acme.sh は多数の DNS API に対応しています。以下は例です(実運用では公式の dnsapi スクリプト名と必要な環境変数を必ず確認してください)。

# Google Cloud DNS(例)
export GCE_Project="your-project-id"
export GCE_Domain="example.com"
acme.sh --issue -d example.com --dns dns_gce

# さくらインターネット(例)
export SAKURA_ACCESS_TOKEN="your-token"
acme.sh --issue -d example.com --dns dns_sakura

# お名前.com(例)
export Onamae_User="your-username"
export Onamae_Pass="your-password"
acme.sh --issue -d example.com --dns dns_onamae

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

ワイルドカード(*.example.com)証明書は、サブドメインが増える運用で非常に便利です。注意点として、Let’s Encrypt でワイルドカードを取るにはDNS-01 が必須です(http-01/standaloneでは取得できません)。

ワイルドカード証明書の発行

# ワイルドカード証明書の取得(DNS-01必須)
acme.sh --issue -d example.com -d "*.example.com" --dns dns_cf

# ネストしたサブドメインを追加したい場合の例
acme.sh --issue \
  -d example.com \
  -d "*.example.com" \
  -d "*.app.example.com" \
  -d "*.api.example.com" \
  --dns dns_cf

証明書のインストール

証明書は「発行しただけ」では使えません。Webサーバーが参照する場所に配置し、更新時に reload されるようにします。–install-cert により、更新後に自動で配置・reload まで実行できます。

# Nginx用の証明書インストール
acme.sh --install-cert -d example.com \
  --key-file /etc/nginx/ssl/example.com.key \
  --fullchain-file /etc/nginx/ssl/example.com.pem \
  --reloadcmd "nginx -t && systemctl reload nginx"

# Apache用の証明書インストール
acme.sh --install-cert -d example.com \
  --cert-file /etc/apache2/ssl/example.com.crt \
  --key-file /etc/apache2/ssl/example.com.key \
  --fullchain-file /etc/apache2/ssl/example.com.pem \
  --reloadcmd "apachectl configtest && systemctl reload apache2"

複数ドメインの一括管理

複数の独立ドメインを運用している場合は、スクリプト化して発行・配置・リロードまで自動化すると安全です。以下は例です(環境変数・権限・パスは必ず実環境に合わせてください)。

#!/bin/bash
# bulk_ssl_issue.sh

set -eu

DOMAINS=(
  "example1.com"
  "example2.com"
  "example3.com"
)

for domain in "${DOMAINS[@]}"; do
  echo "Processing SSL certificate for ${domain} ..."

  # 証明書発行(DNS-01)
  acme.sh --issue -d "${domain}" -d "*.${domain}" --dns dns_cf

  # 証明書インストール
  acme.sh --install-cert -d "${domain}" \
    --key-file "/etc/ssl/private/${domain}.key" \
    --fullchain-file "/etc/ssl/certs/${domain}.pem" \
    --reloadcmd "nginx -t && systemctl reload nginx"

  if [ $? -eq 0 ]; then
    echo "Success: ${domain}"
  else
    echo "Failed: ${domain}"
  fi

  # APIレート制限対策
  sleep 5
done

自動更新の設定

Let’s Encrypt の証明書は短い有効期間のため、更新は自動化が前提です。acme.sh は –cron を定期実行するのが基本で、期限が近い証明書だけを更新します。短い周期(毎日など)で実行しても、更新不要なら何もしないため安全です。

cron設定による自動更新

cron は簡単ですが、ユーザー・パス・権限を間違えると更新に失敗しやすいので、acme.sh の home を明示し、ログも残す設計が安全です。

# crontab の編集
crontab -e

# 毎日 02:17 に更新チェック(例:分を固定にせず、環境により分は分散推奨)
17 2 * * * "/home/user/.acme.sh/acme.sh" --cron --home "/home/user/.acme.sh" >> /var/log/acme_renew.log 2>&1

systemd タイマーによる自動更新

systemd の方がログ管理や実行状態の把握がしやすく、サーバー運用ではおすすめです。以下は最小構成例です(パスは環境に合わせてください)。

# /etc/systemd/system/acme-renewal.service
[Unit]
Description=Renew Let's Encrypt certificates (acme.sh)
After=network-online.target

[Service]
Type=oneshot
ExecStart=/home/user/.acme.sh/acme.sh --cron --home /home/user/.acme.sh
User=root
# /etc/systemd/system/acme-renewal.timer
[Unit]
Description=Daily renewal check for acme.sh
Requires=acme-renewal.service

[Timer]
OnCalendar=daily
Persistent=true

[Install]
WantedBy=timers.target
# 有効化
sudo systemctl daemon-reload
sudo systemctl enable acme-renewal.timer
sudo systemctl start acme-renewal.timer
sudo systemctl status acme-renewal.timer

更新状況の監視

更新が失敗しても「すぐには気づけない」ことが一番危険です。以下は openssl を使って残日数を確認し、一定日数以下なら通知する例です(通知先は必要に応じて差し替えてください)。

#!/bin/bash
# ssl_monitor.sh

set -eu

DOMAINS=(
  "example.com"
  "api.example.com"
  "www.example.com"
)

WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
WARNING_DAYS=14

send_alert() {
  local domain="$1"
  local message="$2"

  curl -sS -X POST -H 'Content-type: application/json' \
    --data "{\"text\":\"${message}\"}" \
    "${WEBHOOK_URL}" >/dev/null
}

check_ssl_expiry() {
  local domain="$1"
  local expiry_date
  local expiry_ts
  local now_ts
  local days_left

  expiry_date=$(echo | openssl s_client -servername "${domain}" -connect "${domain}:443" 2>/dev/null \
    | openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2 || true)

  if [ -z "${expiry_date}" ]; then
    send_alert "${domain}" "SSL証明書の確認に失敗しました: ${domain}"
    return
  fi

  expiry_ts=$(date -d "${expiry_date}" +%s)
  now_ts=$(date +%s)
  days_left=$(( (expiry_ts - now_ts) / 86400 ))

  if [ "${days_left}" -le "${WARNING_DAYS}" ]; then
    send_alert "${domain}" "SSL証明書の期限が近づいています: ${domain}(残り ${days_left} 日)"
  fi
}

for domain in "${DOMAINS[@]}"; do
  check_ssl_expiry "${domain}"
done

WebサーバーでのSSL設定

証明書を取得しても、Webサーバー側で正しく参照していないと HTTPS になりません。以下は Nginx / Apache の代表的な設定例です。ファイルパス(key / fullchain)は、先ほどの –install-cert で指定したパスと一致させてください。

Nginx設定例

# /etc/nginx/sites-available/example.com

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

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

  ssl_certificate     /etc/nginx/ssl/example.com.pem;
  ssl_certificate_key /etc/nginx/ssl/example.com.key;

  ssl_protocols TLSv1.2 TLSv1.3;

  ssl_session_cache shared:SSL:10m;
  ssl_session_timeout 10m;

  add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
  add_header X-Frame-Options DENY;
  add_header X-Content-Type-Options nosniff;

  location / {
    root /var/www/html;
    index index.html index.htm;
  }
}

Apache設定例

# /etc/apache2/sites-available/example.com.conf

<VirtualHost *:80>
  ServerName example.com
  ServerAlias *.example.com
  Redirect permanent / https://example.com/
</VirtualHost>

<VirtualHost *:443>
  ServerName example.com
  ServerAlias *.example.com
  DocumentRoot /var/www/html

  SSLEngine on
  SSLCertificateFile /etc/apache2/ssl/example.com.crt
  SSLCertificateKeyFile /etc/apache2/ssl/example.com.key
  SSLCertificateChainFile /etc/apache2/ssl/example.com.pem

  SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1

  Header always set Strict-Transport-Security "max-age=31536000; includeSubDomains"
  Header set X-Frame-Options DENY
  Header set X-Content-Type-Options nosniff
</VirtualHost>

トラブルシューティング

よくあるエラーと対処法

acme.sh / Let’s Encrypt 周りで多いのは、DNS 伝播、API権限、ポート競合、そして更新タスクの実行ユーザー・パス不一致です。まずは「どの方式で検証しているか」を確認し、方式ごとのチェックを行います。

# DNS-01 の TXT レコード確認
dig TXT _acme-challenge.example.com

# http-01 の場合:.well-known が到達できるか
curl -I http://example.com/.well-known/acme-challenge/test

# 更新の手動実行(強制更新)
acme.sh --renew -d example.com --force

# 削除と再発行(慎重に)
acme.sh --remove -d example.com
acme.sh --issue -d example.com --dns dns_cf

ログ確認とデバッグ

「なぜ失敗しているか」はログに出ます。cron/systemd 実行時は PATH が違うことも多いので、実行したユーザーacme.sh の homeも含めて確認してください。

# acme.sh のログ確認(例)
tail -f ~/.acme.sh/acme.sh.log

# デバッグ(出力を増やす)
acme.sh --issue -d example.com --dns dns_cf --debug 2

# 証明書ファイルの中身確認
openssl x509 -in ~/.acme.sh/example.com/example.com.cer -text -noout

# SSL接続テスト
openssl s_client -connect example.com:443 -servername example.com

証明書の復旧手順

更新が失敗して期限が切れそうな場合、最優先は「現行証明書のバックアップ」→「再発行」→「配置」→「Webサーバーの設定テストとリロード」です。以下は一連の流れをまとめた例です。

#!/bin/bash
# ssl_recovery.sh

set -eu

DOMAIN="example.com"
BACKUP_DIR="/backup/ssl"
LOG_FILE="/var/log/ssl_recovery.log"
WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"

log_message() {
  echo "$(date '+%Y-%m-%d %H:%M:%S'): $1" | tee -a "$LOG_FILE"
}

backup_current_cert() {
  local ts
  ts=$(date +%Y%m%d_%H%M%S)
  log_message "現在の証明書をバックアップ中: ${ts}"
  mkdir -p "${BACKUP_DIR}/${ts}"
  cp -r ~/.acme.sh/"${DOMAIN}"/ "${BACKUP_DIR}/${ts}/" || true
}

reissue_certificate() {
  log_message "証明書の再取得を開始"
  acme.sh --remove -d "${DOMAIN}" || true

  if acme.sh --issue -d "${DOMAIN}" -d "*.${DOMAIN}" --dns dns_cf; then
    log_message "証明書取得成功"
    return 0
  fi

  log_message "証明書取得失敗"
  return 1
}

install_certificate() {
  log_message "証明書のインストール中"
  acme.sh --install-cert -d "${DOMAIN}" \
    --key-file /etc/nginx/ssl/"${DOMAIN}".key \
    --fullchain-file /etc/nginx/ssl/"${DOMAIN}".pem \
    --reloadcmd "nginx -t && systemctl reload nginx"
}

notify() {
  local message="$1"
  curl -sS -X POST -H 'Content-type: application/json' \
    --data "{\"text\":\"${message}\"}" \
    "${WEBHOOK_URL}" >/dev/null || true
}

main() {
  log_message "SSL証明書復旧処理を開始: ${DOMAIN}"
  backup_current_cert

  if reissue_certificate && install_certificate; then
    log_message "SSL証明書復旧完了"
    notify "SSL証明書の復旧が完了しました: ${DOMAIN}"
    exit 0
  else
    log_message "SSL証明書復旧失敗"
    notify "SSL証明書の復旧に失敗しました: ${DOMAIN}"
    exit 1
  fi
}

main "$@"

セキュリティ強化

OCSP Staplingの設定

OCSP Stapling を有効化すると、証明書失効情報の確認をサーバー側で補助でき、クライアントの負荷や接続遅延を減らせることがあります。環境により効果は異なるため、まずは設定→検証の順で導入してください。

# Nginx でのOCSP Stapling(例)

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

  ssl_certificate /etc/nginx/ssl/example.com.pem;
  ssl_certificate_key /etc/nginx/ssl/example.com.key;

  ssl_stapling on;
  ssl_stapling_verify on;
  ssl_trusted_certificate /etc/nginx/ssl/example.com.pem;

  resolver 1.1.1.1 8.8.8.8 valid=300s;
  resolver_timeout 5s;
}

Certificate Transparency監視

CT(Certificate Transparency)ログを監視すると、自ドメイン名で「見覚えのない証明書」が発行されていないかを早期検知できます。以下は簡易スクリプト例です(jq が必要です)。

#!/bin/bash
# ct_monitor.sh

set -eu

DOMAIN="example.com"
CT_API="https://crt.sh/?q=%25.${DOMAIN}&output=json"
KNOWN_CERTS_FILE="/tmp/known_certs_${DOMAIN//./_}.txt"
WEBHOOK_URL="https://hooks.slack.com/services/YOUR/WEBHOOK/URL"

send_alert() {
  local message="$1"
  curl -sS -X POST -H 'Content-type: application/json' \
    --data "{\"text\":\"${message}\"}" \
    "${WEBHOOK_URL}" >/dev/null
}

new_certs=$(curl -s "${CT_API}" | jq -r '.[].id' | sort -n | uniq)

if [ ! -f "${KNOWN_CERTS_FILE}" ]; then
  echo "${new_certs}" > "${KNOWN_CERTS_FILE}"
  exit 0
fi

unknown=$(comm -13 "${KNOWN_CERTS_FILE}" <(echo "${new_certs}") || true)
if [ -n "${unknown}" ]; then
  count=$(echo "${unknown}" | wc -l | tr -d ' ')
  send_alert "新しいSSL証明書が検出されました: ${DOMAIN}(${count} 件)"
  echo "${new_certs}" > "${KNOWN_CERTS_FILE}"
fi

SSL設定の品質チェック

設定変更のたびに、TLSバージョンやチェーンが壊れていないかを機械的にチェックできると安全です。外部サービスのAPIを使う場合は、API制限・利用規約・情報送信(対象ドメイン)に注意してください。

パフォーマンス最適化

SSL設定の最適化

TLS の最適化は「速さ」だけでなく「互換性」も絡みます。まずは TLSv1.2/1.3 を有効にし、HTTP/2 を併用し、問題が出ない範囲でチューニングしてください。以下は設定例です(環境により調整が必要です)。

# 例:Nginx の TLS まわり(環境により要調整)
ssl_protocols TLSv1.2 TLSv1.3;

ssl_session_cache shared:SSL:50m;
ssl_session_timeout 1d;
ssl_session_tickets off;

ssl_buffer_size 4k;

証明書チェーンの最適化

「中間証明書が足りない」「fullchain ではなく cert だけを設定している」などは、環境によって接続エラーや警告の原因になります。基本は fullchain を参照する設定にして、openssl で実際のチェーンを確認してください。

# サーバーが返す証明書の確認
openssl s_client -connect example.com:443 -servername example.com < /dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer

よくある質問(FAQ)

Q. ワイルドカード証明書の更新でエラーが発生します

A. DNS-01 の API 権限、環境変数が cron/systemd 実行時に読めているか、TXT の伝播待ち時間を確認してください。Cloudflare の場合は API トークン方式(最小権限)に切り替えると管理が楽になることがあります。

Q. 複数のサブドメインで別々の証明書が必要ですか?

A. ワイルドカード証明書(*.example.com)を使えば、多くの場合は1枚で複数サブドメインをカバーできます。ただし、別ドメイン(example.net など)は別証明書が必要です。

Q. 証明書の更新に失敗した場合の対処法は?

A. まずログ(~/.acme.sh/acme.sh.log や systemd journal)を確認し、DNS設定・API認証・レート制限・Webサーバーの reload 失敗(nginx -t など)を切り分けます。緊急時は「復旧手順」スクリプトの流れ(バックアップ→再発行→配置→テスト→reload)で復旧します。

おすすめサーバー環境

SSL証明書の運用は「自動更新が動くこと」と「DNS設定やリロードが安全にできること」が重要です。安定稼働、バックアップ、監視などを含めて、運用しやすい環境を選んでください:

目次