証明書の期限切れが引き起こす問題
SSL/TLS証明書の有効期限が切れると、ブラウザは即座に「接続が安全ではありません」という警告を表示します。ユーザーはページを開けなくなるだけでなく、REST API の呼び出しや Webhook の受信なども証明書エラーで止まります。証明書の更新作業そのものは数分で完了しますが、切れてから気づくと、その間ずっとサービスが停止した状態になります。
Let’s Encrypt のように有効期間が90日と短い証明書は、自動更新(certbot renew)が正しく機能しているかどうかの確認も兼ねて、定期的に期限を把握しておくことが大切です。本記事では openssl コマンドを使い、リモートホストに対してもローカルの証明書ファイルに対しても有効期限を調べる方法を紹介します。
リモートホストの証明書期限を確認する
稼働中のサーバーから直接証明書の期限を調べるには、openssl s_client でサーバーに接続して証明書を受け取り、openssl x509 で日付情報を取り出します。
openssl s_client -connect example.com:443 </dev/null 2>/dev/null \
| openssl x509 -noout -dates
実行すると次のような出力が得られます。
notBefore=Nov 1 00:00:00 2025 GMT
notAfter=Jan 30 23:59:59 2026 GMT
notAfter の日付が有効期限です。終了日だけを取り出したい場合は -dates の代わりに -enddate を使います。
openssl s_client -connect example.com:443 </dev/null 2>/dev/null \
| openssl x509 -noout -enddate
1つの IP アドレスに複数のバーチャルホストが同居している環境では、-servername オプションで対象ドメインを明示しないと、意図したドメインの証明書が返ってこない場合があります。バーチャルホスト構成のサーバーを確認するときは、次のように -servername を合わせて指定してください。
openssl s_client -connect example.com:443 -servername example.com \
</dev/null 2>/dev/null \
| openssl x509 -noout -enddate
HTTPS(443番ポート)以外の場所で証明書を使っている場合は、ポート番号を変更してください。IMAPS は 993、SMTPS は 465 が一般的です。
ローカルの証明書ファイルを直接確認する
Let’s Encrypt で取得した証明書はデフォルトで /etc/letsencrypt/live/ドメイン名/cert.pem に配置されています。ファイルを直接読んで期限を確認するには次のコマンドを使います。
openssl x509 -noout -dates -in /etc/letsencrypt/live/example.com/cert.pem
Let’s Encrypt 以外の証明書(.crt や .pem 形式)も同様に確認できます。
openssl x509 -noout -dates -in /etc/ssl/certs/example.crt
PKCS#12 形式(拡張子 .p12 や .pfx)の証明書は openssl x509 で直接読めません。次のようにいったん PEM 形式に変換してから確認します。パスフレーズの入力を求められた場合はコマンドを対話的に実行してください。
openssl pkcs12 -in example.p12 -nokeys -clcerts \
| openssl x509 -noout -dates
残り日数を数値として取り出す
「あと何日で期限が切れるか」を数値として取り出すには、openssl が返す日付を Unix タイムスタンプに変換して現在時刻と比較します。次のコマンドで残り日数を確認できます。
EXPIRY=$(openssl s_client -connect example.com:443 -servername example.com \
</dev/null 2>/dev/null \
| openssl x509 -noout -enddate \
| cut -d= -f2)
DAYS=$(( ( $(date -d "$EXPIRY" +%s) - $(date +%s) ) / 86400 ))
echo "${DAYS} 日後に期限切れ"
date -d は GNU date(通常の Linux ディストリビューションに付属)の書き方です。macOS など BSD 系環境では date -j -f "%b %d %T %Y %Z" "$EXPIRY" +%s のように記述が異なります。
複数ドメインを一括でチェックするスクリプト
管理するドメインが複数ある場合、次の想定例のようなスクリプトを参考に一括チェックを自動化できます。期限まで指定した日数を切ったドメインを警告として出力します。
#!/bin/bash
THRESHOLD=30 # 残り日数がこの値以下で警告
DOMAINS=(
"example.com"
"sub.example.com"
"another-site.example"
)
for DOMAIN in "${DOMAINS[@]}"; do
EXPIRY=$(openssl s_client -connect "${DOMAIN}:443" -servername "${DOMAIN}" \
</dev/null 2>/dev/null \
| openssl x509 -noout -enddate 2>/dev/null \
| cut -d= -f2)
if [ -z "$EXPIRY" ]; then
echo "[ERROR] ${DOMAIN}: 証明書を取得できませんでした"
continue
fi
DAYS=$(( ( $(date -d "$EXPIRY" +%s) - $(date +%s) ) / 86400 ))
if [ "$DAYS" -le "$THRESHOLD" ]; then
echo "[WARN] ${DOMAIN}: 残り ${DAYS} 日(期限: ${EXPIRY})"
else
echo "[OK] ${DOMAIN}: 残り ${DAYS} 日"
fi
done
このスクリプトを cron に登録し、出力をメールや Slack の Incoming Webhook に流すと、定期的な監視の仕組みとして機能します。THRESHOLD の値は更新作業に必要なリードタイムに合わせて調整してください。Let’s Encrypt のように自動更新を設定している場合でも、更新が実際に成功しているかどうかを定期確認するチェックとして有効です。
よくあるつまずきと注意点
- 何も出力されない・空になる:
2>/dev/nullでエラーを捨てているため、接続に失敗してもエラーメッセージが表示されません。まず2>/dev/nullを外して実行し、エラー内容を確認してください。ファイアウォールや ACL でポートが閉じられていることがよく見られる原因の一つです。 - SNI 未設定で意図したドメインの証明書が返らない:バーチャルホストを使っているサーバーでは
-servernameオプションが必要です。付け忘れるとデフォルトの(意図しない)証明書が返ってくる場合があります。 - StartTLS が必要なサービスでエラーになる:SMTP(25・587)・IMAP(143)・POP3(110)など、平文から TLS に切り替える StartTLS プロトコルを使うサービスには
-starttls smtp(または-starttls imap)オプションが必要です。 - date -d が動かない:Alpine Linux など
busyboxベースの環境には GNU date が含まれていません。apk add coreutilsで追加するか、Python のdatetimeモジュールを使う方法に書き直すと対応できます。 - 中間証明書の期限も確認したい:
openssl s_clientに-showcertsを付けると、チェーン全体の証明書が PEM ブロックとして出力されます。各ブロックを切り出してopenssl x509 -noout -datesに渡すことで、中間証明書の有効期限を個別に確認できます。
「コマンドは打てる。次は”現場で通用する型”を最短で身につけたい」——そんなあなたへ。
ネットの断片的なTipsをコピペするだけでは、体系的な運用スキルはなかなか積み上がりません。リナックスマスター.JPの無料メルマガ「リナマガ」では、現場で実際に使うLinuxサーバー運用の考え方を、順を追って実務目線でお届けしています。いま登録された方には、安全なサーバー構築の「型」をまとめた『Linuxサーバー構築入門マニュアル(図解60P)』を完全無料でプレゼント中です。
>> 図解60P『Linux入門マニュアル』を無料で受け取る
(メルマガ登録は10秒・いつでも解除OK)
※ 「独学の時間がもったいない」「プロから現場の技術を最短で学びたい」という本気の方には、2日で実務レベルが身につく初心者向けハンズオンセミナーもご用意しています。
