本ページはプロモーションが含まれています

Python

SSL証明書の期限切れをPythonで監視|Discordに通知する

CATEGORY
REV.
DRAWN BY
TOM
SHEETS
8枚

トム

SESで約8年、環境を変えられないもどかしさの中にいました。32歳で自社開発企業に転職し、今はClaude Codeと自動化で、仕事も時間も組み直している途中です。
・Javaバックエンドエンジニア(Java歴12年以上)
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎
詳細なプロフィール

𝕏

監視スクリプトは毎週ちゃんと動いていました。

ただし3か月のあいだ、見張っていたのは自分のブログではなく example.com でした。

SSL証明書の期限切れは、Pythonの標準ライブラリだけで監視できます。

証明書の期限を読み、残り日数が閾値を割ったときだけDiscordに通知する仕組みを、私は2026年5月から自宅のMacで動かしています。

この記事では、そのスクリプトをコピペで動く形にしたものと、約半年分のログで分かった「閾値30日の落とし穴」までまとめました。

2つのブログ(code-rebuild.com・systradefx.com)を実際に見張っている、Python 3.9.6で動作確認した版です。

この記事でわかること

  • SSL証明書の期限をopensslとPythonで確かめる方法
  • 期限が近いときだけDiscordに通知するPythonスクリプト(標準ライブラリのみ)
  • launchd・cronで週1回自動で動かす設定
  • 有効期間が47日まで縮む2029年に向けた、閾値の決め方

PARTS LIST /目次

SSL証明書の期限切れはPythonの標準ライブラリで監視できる

SSL証明書の期限は、Pythonの ssl と socket だけで取り出せます。

追加のライブラリを入れる必要はなく、通知も標準の urllib でDiscordのWebhookに送れます。

外部の監視サービスに頼らずに済むので、設定ファイル1つとスクリプト1本で完結するのがいいところです。

ただ、作り始める前に「そもそも監視は要るのか」を一度考えておくと、閾値の決め方で迷わなくなります。

SSL証明書の有効期限とは?切れるとサイトはどうなるか

SSL証明書の有効期限とは、認証局が「この証明書を信頼してよい」と保証する期間の終わりの日時です。

証明書の中に notAfter という項目で書き込まれていて、後から延ばせません。

延ばしたいときは、新しい証明書を発行して差し替えるしかない仕組みです。

期限が切れると、ブラウザはページを開く前に警告画面を出します。

多くの訪問者はそこで引き返すので、ブログならアクセスがほぼ止まります。

HSTSを設定したサイトでは警告を無視して進むボタンすら出ません。

APIとして呼ばれているサーバーなら、呼び出し側が証明書の検証エラーで止まり、連携先まで巻き込みます。

自分で監視が必要になった2つの理由

理由の1つめは、Let's Encryptの期限切れ通知メールが2025年6月4日に終わったことです。

自動更新が失敗していても、以前ならメールで気づけました。

今は、その最後の網がありません。

2つめは、証明書の有効期間そのものが短くなっていることです。

期間が短いほど更新の回数が増え、1回の失敗から期限切れまでの猶予も縮みます。

変化時期影響
Let's Encryptの期限通知メール終了2025年6月4日自動更新の失敗にメールで気づけない
証明書の最大有効期間が200日に2026年3月15日年1回だった更新が年2回に
最大有効期間が100日に2027年3月15日年4回近く更新
最大有効期間が47日に2029年3月15日年8回近く更新

自動更新を設定していれば安心、とは言い切れなくなりました。

更新が「動いているはず」と「動いた」の間を埋めるのが、期限の監視です。

まずはSSL証明書の期限を手で確認する方法

監視を組む前に、手元で1回期限を表示しておくと、スクリプトの結果が正しいか比べられます。

確認方法はopensslコマンドとPythonの2通りで、どちらも1分かかりません。

Macならopensslは最初から入っていて、Pythonも python3 で動きます。

同じドメインで両方を試し、日付が一致するのを見てから先に進むのがおすすめです。

opensslコマンドで有効期限を表示する

opensslの s_client でサーバーにつなぎ、受け取った証明書の日付だけを表示します。

echo | openssl s_client -connect code-rebuild.com:443 -servername code-rebuild.com 2>/dev/null | openssl x509 -noout -issuer -dates

2026年10月30日に実行した結果がこちらです。

issuer=C=US, O=Google Trust Services, CN=WE1
notBefore=Oct 23 10:18:12 2026 GMT
notAfter=Jan 21 11:18:11 2027 GMT

notAfter が有効期限で、時刻はGMT(日本時間の9時間前)です。

-servername を付けないと、1台のサーバーに複数のドメインが同居している環境で別の証明書が返ってくることがあります。

シェルスクリプトで判定したい場合は、openssl x509 -noout -checkend 2592000 で「30日(2,592,000秒)以内に切れるか」を終了コードで受け取れます。

Pythonの10行で残り日数を出す

Pythonなら、残り日数の計算までまとめて書けます。

import socket
import ssl
from datetime import datetime, timezone

host = "code-rebuild.com"
context = ssl.create_default_context()
with socket.create_connection((host, 443), timeout=10) as sock:
    with context.wrap_socket(sock, server_hostname=host) as ssock:
        cert = ssock.getpeercert()
not_after = datetime.fromtimestamp(ssl.cert_time_to_seconds(cert["notAfter"]), tz=timezone.utc)
print(host, "残り", (not_after - datetime.now(timezone.utc)).days, "日")

ssl.cert_time_to_seconds() は、証明書の 'Jan 21 11:18:11 2027 GMT' という形の日付をUNIX時刻に直す標準の関数です。

strptime で書式を指定して読むより、書き間違いの心配がありません。

これを複数ドメインに広げ、通知を付けたものが次のスクリプトです。

Pythonで期限を監視してDiscordに通知する4ステップ

監視スクリプトは「設定を読む→期限を取る→判定する→通知する」の4段で組みます。

私の実運用版は共通モジュール(ログ・設定の検証・エラー通知)に依存しているので、ここではそれを外し、ファイル2つだけで動く形に書き直しました。

コードは記事の内容に合わせて手直ししていますが、判定の考え方と閾値は実運用版と同じです。

step
1
監視するドメインと閾値をJSONに書く

対象を設定ファイルに分けておくと、ドメインが増えてもコードを触らずに済みます。

step
2
証明書の期限を取得して残り日数を計算する

ドメインごとに接続し、notAfter から残り日数を出します。

step
3
期限切れと接続失敗を分けて判定する

期限切れ・証明書の不一致・接続できない、の3つを別のメッセージにします。

step
4
DiscordのWebhookに結果を送る

閾値を割ったものがあるときだけ、まとめて1通送ります。

監視するドメインと閾値をJSONに書く

設定は domains.json に置き、ドメインごとに閾値を変えられるようにします。

{
  "default_warn_days": 30,
  "domains": [
    {"host": "code-rebuild.com"},
    {"host": "systradefx.com", "warn_days": 45},
    {"host": "api.example.com", "port": 8443}
  ]
}

warn_days を省いたドメインには default_warn_days が使われます。

ポートが443以外のサーバーは port で指定します。

私の実運用版も同じ形で、2つのブログを閾値30日で見ています。

証明書の期限を取得して残り日数を計算する

スクリプト本体の全体です。

domains.json と同じフォルダに ssl_monitor.py として保存します。

"""SSL証明書の期限を調べて、危ないものだけDiscordに通知する"""
import json
import os
import socket
import ssl
from datetime import datetime, timezone
from pathlib import Path
from urllib.request import Request, urlopen

CONFIG = Path(__file__).with_name("domains.json")


def fetch_not_after(host, port=443, timeout=10):
    """証明書の有効期限(notAfter)をUTCのdatetimeで返す"""
    context = ssl.create_default_context()
    with socket.create_connection((host, port), timeout=timeout) as sock:
        with context.wrap_socket(sock, server_hostname=host) as ssock:
            cert = ssock.getpeercert()
    # 'Jan 21 11:18:11 2027 GMT' の形をUNIX時刻に直す
    return datetime.fromtimestamp(ssl.cert_time_to_seconds(cert["notAfter"]), tz=timezone.utc)


def check(entry, default_warn_days):
    """1ドメインを調べ、通知が必要なら1行のメッセージを、正常ならNoneを返す"""
    host = entry["host"]
    warn_days = entry.get("warn_days", default_warn_days)
    try:
        not_after = fetch_not_after(host, entry.get("port", 443))
    except ssl.SSLCertVerificationError as e:
        # 期限切れの証明書は中身を読む前に検証で弾かれる(10 = 期限切れ)
        if e.verify_code == 10:
            return f"❌ {host} 期限切れ"
        return f"⚠️ {host} 証明書の検証に失敗: {e.verify_message}"
    except OSError as e:
        return f"⚠️ {host} 接続できない: {e}"
    days_left = (not_after - datetime.now(timezone.utc)).days
    print(f"{host} 残り{days_left}日({not_after:%Y-%m-%d}まで)")
    if days_left <= warn_days:
        return f"⚠️ {host} 残り{days_left}日({not_after:%Y-%m-%d}まで)"
    return None


def notify(webhook_url, text):
    """DiscordのWebhookにテキストを送る"""
    body = json.dumps({"content": text}).encode("utf-8")
    headers = {"Content-Type": "application/json", "User-Agent": "ssl-monitor/1.0"}
    with urlopen(Request(webhook_url, data=body, headers=headers), timeout=10):
        pass


def main():
    config = json.loads(CONFIG.read_text(encoding="utf-8"))
    default_warn_days = config.get("default_warn_days", 30)
    alerts = [msg for entry in config["domains"] if (msg := check(entry, default_warn_days))]
    webhook_url = os.environ.get("DISCORD_WEBHOOK_URL")
    if alerts and webhook_url:
        notify(webhook_url, "SSL証明書の確認が必要です\n" + "\n".join(alerts))
    for msg in alerts:
        print(msg)


if __name__ == "__main__":
    main()

(msg := check(...)) の代入式を使っているので、Python 3.8以降で動きます。

DISCORD_WEBHOOK_URL が未設定のときは通知せず、結果を画面に出すだけです。

動作確認用に、わざと壊した証明書を返す badssl.com のテスト用ドメインを混ぜて実行した結果がこちらです。

code-rebuild.com 残り82日(2027-01-21まで)
systradefx.com 残り82日(2027-01-21まで)
❌ expired.badssl.com 期限切れ
⚠️ wrong.host.badssl.com 証明書の検証に失敗: Hostname mismatch, certificate is not valid for 'wrong.host.badssl.com'.
⚠️ no-such-host.invalid 接続できない: [Errno 8] nodename nor servname provided, or not known

期限切れの証明書は「取得失敗」になる落とし穴

期限切れの判定には、残り日数のマイナスではなく、検証エラーの番号を使います。

ssl.create_default_context() は証明書を検証するので、期限切れの証明書は getpeercert() で中身を読む前に、接続の段階で SSLCertVerificationError になるからです。

判定の流れを図にすると、次のようになります。

flowchart TD
    A["ドメインに接続"] --> B{"証明書の検証"}
    B -->|"成功"| C["notAfterから残り日数を計算"]
    C --> D{"残り日数が閾値以下?"}
    D -->|"はい"| E["⚠️ 残りN日として通知"]
    D -->|"いいえ"| F["正常(通知しない)"]
    B -->|"失敗:verify_code 10"| G["❌ 期限切れとして通知"]
    B -->|"失敗:それ以外"| H["⚠️ 検証失敗として通知"]
    A -->|"つながらない"| I["⚠️ 接続できないとして通知"]
ハマりポイント
「残り日数がマイナスなら期限切れ」の分岐は、検証ありの接続では通らない

私の実運用版にも、残り日数が0未満なら「期限切れ」と出す分岐があります。ところが、テスト用の期限切れドメインで試すと、出てきたのは「取得失敗 — certificate has expired」でした。検証で接続が止まるので、残り日数を計算する行まで届かないためです。通知は届くので実害はありませんが、期限切れをはっきり出したいなら verify_code の10(期限切れ)で分けてください。

検証を切って(ssl.CERT_NONE)中身を読む方法もあります。

ただ、その設定では getpeercert() が空の辞書を返すので、日付を読むには別のライブラリで証明書を解析する手間が増えます。

期限切れかどうかを知るだけなら、エラー番号で十分です。

DiscordのWebhookに結果を送る

Discordへの通知は、Webhook URLに {"content": "本文"} のJSONをPOSTするだけです。

Webhook URLは、Discordのチャンネル設定の「連携サービス」から「ウェブフックを作成」で発行できます。

URLを知っている人なら誰でも投稿できてしまうので、コードには書かず環境変数で渡します。

export DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/xxxxx/yyyyy"
python3 ssl_monitor.py

サンプルで User-Agent を自分で付けているのには理由があります。

DiscordのAPIは公式ドキュメントでリクエストに User-Agent を付けるよう求めていて、urllib の既定値(Python-urllib/3.x)のままだと403で弾かれることがあります。

送信で403が返ってきたら、まずここを疑ってください。

全件正常のときは通知しない
私の実運用版は、閾値を割ったものがあるときだけ通知し、正常時は送りません。毎週「異常なし」が届くと、本当の警告まで読み流すようになるからです。動いているかどうかはログで確かめます。

launchdやcronで週1回自動で動かす

監視は、人が覚えていなくても動く状態にして初めて意味があります。

Macならlaunchd、Linuxサーバーならcronに登録するのが手軽です。

私は週1回、月曜の朝4時に動かしています。

証明書の期限は1日で急に変わるものではないので、90日の証明書なら週1回で足ります(有効期間が短くなったときの頻度は後半で書きます)。

Macならlaunchdで毎週月曜4時に実行する

~/Library/LaunchAgents/ に次のplistを置きます。

パスとWebhook URLは自分の環境に置き換えてください。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>Label</key>
    <string>com.example.ssl_monitor</string>
    <key>ProgramArguments</key>
    <array>
        <string>/usr/bin/python3</string>
        <string>/Users/you/ssl_monitor/ssl_monitor.py</string>
    </array>
    <key>EnvironmentVariables</key>
    <dict>
        <key>DISCORD_WEBHOOK_URL</key>
        <string>https://discord.com/api/webhooks/xxxxx/yyyyy</string>
    </dict>
    <key>StartCalendarInterval</key>
    <dict>
        <key>Weekday</key>
        <integer>1</integer>
        <key>Hour</key>
        <integer>4</integer>
        <key>Minute</key>
        <integer>0</integer>
    </dict>
    <key>StandardOutPath</key>
    <string>/Users/you/ssl_monitor/ssl_monitor.log</string>
    <key>StandardErrorPath</key>
    <string>/Users/you/ssl_monitor/ssl_monitor.log</string>
</dict>
</plist>

登録は launchctl bootstrap gui/$(id -u) ~/Library/LaunchAgents/com.example.ssl_monitor.plist、すぐに試すなら launchctl kickstart gui/$(id -u)/com.example.ssl_monitor です。

Weekday の1が月曜で、0か7が日曜にあたります。

Macが4時にスリープしていた場合は、起きたあとにまとめて1回実行されます。

plistの書き方や登録・解除の手順は、Mac launchdの使い方|plistで定期実行を自動化する手順にまとめています。

Linuxサーバーならcronに1行足す

サーバー上で動かすなら、cronに1行足すだけです。

0 4 * * 1 DISCORD_WEBHOOK_URL="https://discord.com/api/webhooks/xxxxx/yyyyy" /usr/bin/python3 /home/you/ssl_monitor/ssl_monitor.py >> /home/you/ssl_monitor/ssl_monitor.log 2>&1

crontab -e で開いて末尾に追記します。

先頭の 0 4 * * 1 が「毎週月曜の4時0分」です。

ただ、見張る対象のサーバー自身で動かすと、サーバーが落ちたときに監視も一緒に止まります。

できれば手元のMacや別のサーバーなど、見張られる側とは別の場所に置くのが安全です。

約半年動かしたログで分かった3つのこと

監視を入れた2026年5月9日から10月26日までのログを読み返すと、作った当初は想像していなかった事実が3つ出てきました。

どれもスクリプトが毎週残していた notAfter と残り日数の記録から分かったものです。

コードの書き方よりも、運用の置き方に関わる話になります。

3か月間example.comを見張っていた

ログの最初のほうは、ずっと同じ1行でした。

LOG2026-05-09〜08-03監視対象が見本の example.com のまま

動作確認のために入れた example.com が、本物の設定ファイルに残ったままでした。ジョブは毎週月曜に動き、「全ドメイン正常: 1件」と記録し続けます。自分のブログは一度も見ていません。2つのブログに書き換えたのは8月8日。エラーが1件も出ないので、ログを眺めても異常には見えない状態でした。

監視が「動いている」と「正しいものを見ている」は別物です。

入れた直後に一度、閾値を大きくして(たとえば "warn_days": 365)わざと通知を出し、自分のドメイン名が届くかを確かめておくと、同じ失敗を防げます。

自動更新は残り30日前後で走るので閾値30日は際どい

設定を直して8月8日に手で流すと、code-rebuild.com の残りは29日で、いきなり閾値の30日を割りました。

ところが2日後の8月10日の定期実行では、残りが89日に戻っていました。

サーバー側の自動更新が、ちょうどそのあいだに走ったわけです。

ログから、更新の前後を拾うとこうなります。

ドメイン更新前の記録更新後の記録
code-rebuild.com8月8日:残り29日8月10日:残り89日
systradefx.com8月24日:残り32日8月31日:残り86日
code-rebuild.com10月5日:残り33日10月12日:残り87日

90日の証明書が、どれも残り30日前後で更新されていました。

閾値を30日にすると、正常な更新の直前に警告が出たり出なかったりする位置に線を引いていることになります。

警告が出ても数日以内に更新されれば正常なので、閾値を自動更新のタイミングより少し内側(たとえば21日)に寄せたほうが、誤報は減ります。

サーバー移行で証明書の発行元と期限が変わった

10月23日にブログをWordPressから静的サイト(Cloudflare Pages)へ移した日、証明書も丸ごと入れ替わっていました。

10月26日のログでは、2つのドメインの期限がそろって2027年1月21日になっています。

opensslで見ると、発行元は Google Trust Services、有効期間は10月23日からの90日でした。

LOG2026-10-26移行後、2つのブログの期限が同じ日になった

移行前の10月19日の記録では、2つのドメインの期限は1か月半ほどずれていました(2026年11月25日と2027年1月7日)。移行後は同じ日付に並んでいます。証明書を用意するのが、契約していたレンタルサーバーから配信元のCloudflare側に変わったためです。監視スクリプトの設定は何も変えておらず、新しい期限をそのまま拾っていました。

サーバーやCDNを移すと、証明書の発行元・期間・更新の仕組みが一度に変わります。

移行作業のチェックリストに「証明書の期限を監視で確かめる」を1行入れておくと安心です。

移行そのものの話はWordPressからAstroへ移行|ブログ2つを1日で静的化に書きました。

有効期間47日時代に向けた閾値の決め方

「残り30日で通知」という決め方は、90日の証明書を前提にしています。

有効期間が47日になれば、新しい証明書でも発行から17日後には残り30日を切るので、毎回警告が出る監視になります。

では、何を基準に線を引けばいいのか。

答えは「残り日数」ではなく「有効期間のうち、どれだけ残っているか」で判定することです。

2026年3月から最大200日、2029年3月から47日へ

証明書の最大有効期間は、CA/Browser Forumで2025年4月に可決されたルール(Ballot SC-081v3)で段階的に縮みます。

無料証明書の代表であるLet's Encryptも、独自に短縮の予定を出しています。

時期内容
2026年3月15日〜業界ルールの最大有効期間が398日から200日に
2026年5月13日〜Let's Encryptが希望者向けプロファイルで45日の証明書を発行
2027年2月10日〜Let's Encryptの標準が90日から64日に
2027年3月15日〜業界ルールの最大有効期間が100日に
2028年2月16日〜Let's Encryptの標準が45日に
2029年3月15日〜業界ルールの最大有効期間が47日に

レンタルサーバーの無料SSLは、事業者が自動で更新してくれるので、利用者の作業は増えないところがほとんどです。

変わるのは「更新に失敗してから期限切れまでの猶予」のほうで、ここが監視の閾値に直接効いてきます。

残り日数ではなく有効期間の割合で判定する

証明書には有効期間の始まり(notBefore)も書かれているので、残りの割合を計算できます。

import socket
import ssl
from datetime import datetime, timezone


def fetch_validity(host, port=443, timeout=10):
    """証明書の有効期間の始まりと終わりをUTCで返す"""
    context = ssl.create_default_context()
    with socket.create_connection((host, port), timeout=timeout) as sock:
        with context.wrap_socket(sock, server_hostname=host) as ssock:
            cert = ssock.getpeercert()
    to_dt = lambda s: datetime.fromtimestamp(ssl.cert_time_to_seconds(s), tz=timezone.utc)
    return to_dt(cert["notBefore"]), to_dt(cert["notAfter"])


def remaining_ratio(host):
    """有効期間のうち、残っている割合(0.0〜1.0)を返す"""
    not_before, not_after = fetch_validity(host)
    now = datetime.now(timezone.utc)
    return (not_after - now) / (not_after - not_before)


ratio = remaining_ratio("code-rebuild.com")
print(f"残り {ratio:.0%}")
if ratio < 0.25:
    print("自動更新が止まっている可能性あり")

更新ツールの多くは、残りが有効期間の3分の1ほどになったら更新します。

たとえばcertbotは4.0から、固定の30日ではなく「残り3分の1」で更新する動きに変わりました。

私が観察した90日の証明書が残り30日前後で更新されていたのも、ちょうど3分の1にあたります。

そこで、残りが4分の1を切ったら「更新されるはずの時期を過ぎても更新されていない」とみなして通知します。

有効期間ごとの目安は次のとおりです。

有効期間更新される目安(残り3分の1)通知の線(残り4分の1)
200日残り約66日残り50日
90日残り30日残り約22日
47日残り約15日残り約11日

47日の証明書なら、週1回の監視では遅い
通知の線が残り約11日だと、週1回の実行では線を越えてから気づくまでに最大6日かかり、手を打てる日が5日ほどしか残りません。有効期間が短い証明書を見張るなら、実行は1日1回にしておくのが無難です。

私の実運用版は、まだ残り日数30日の固定です。

今見ているのは90日の証明書なので、この固定値でも毎週の警告にはなっていません。

切り替えを考える目安は、Let's Encryptの標準が64日になる2027年2月です。

SSL証明書の期限監視でよくある質問

SSL証明書の期限の確認や監視について、つまずきやすい点をまとめました。

確認方法・監視の頻度・通知の仕組みなど、本文で扱った内容を質問の形で整理しています。

細かい手順やコードは、各見出しの本文もあわせて確認してください。

Q1. SSL証明書の期限はどうやって確認できますか?

A1. ブラウザのアドレスバーの鍵アイコンから証明書を開くと、有効期間が表示されます。

コマンドなら echo | openssl s_client -connect ドメイン:443 -servername ドメイン 2>/dev/null | openssl x509 -noout -dates で notAfter に期限が出ます。

Q2. SSL証明書の期限が切れるとどうなりますか?

A2. ブラウザがページを開く前に警告画面を出し、多くの訪問者がそこで離脱します。

HSTSを設定したサイトは警告を無視して進めなくなり、APIの呼び出し側は証明書の検証エラーで通信できなくなります。

Q3. 自動更新を設定していれば監視は不要ですか?

A3. いいえ、自動更新が失敗したときに気づく手段として監視は残したほうが安全です。

Let's Encryptの期限通知メールは2025年6月4日に終わったので、更新の失敗にメールで気づく方法はなくなりました。

Q4. 期限の何日前に通知すればいいですか?

A4. 90日の証明書なら残り21〜30日が目安です。

自動更新は残り約3分の1で走ることが多いので、有効期間が短い証明書では「残りが有効期間の4分の1を切ったら通知」のように割合で決めると、期間が変わっても調整がいりません。

Q5. Pythonで期限切れの証明書の日付は読めますか?

A5. ssl.create_default_context() のまま接続すると、日付を読む前に SSLCertVerificationError になります。

期限切れかどうかを知るだけなら、例外の verify_code が10かどうかで判定できます。

Q6. 監視はどのくらいの頻度で動かせばいいですか?

A6. 90日の証明書なら週1回で足ります。

47日のように有効期間が短い証明書では、通知の線を越えてから気づくまでの遅れを縮めるため、1日1回にしておくと安心です。

Q7. DiscordのWebhookに送ると403になるのはなぜですか?

A7. User-Agent ヘッダーが原因のことが多いです。

Pythonの urllib は既定で Python-urllib/3.x を送るので、"User-Agent": "ssl-monitor/1.0" のように自分で付けると通ります。

SSL証明書の期限切れを防ぐ監視のまとめ

SSL証明書の期限監視は、Pythonの標準ライブラリとDiscordのWebhookだけで組めます。

大事なのはコードの量ではなく、正しい相手を見ているか、どこに線を引くかのほうでした。

  • 期限は ssl と socket で読み、ssl.cert_time_to_seconds() で日付に直す
  • 期限切れは残り日数ではなく、検証エラーの verify_code(10)で判定する
  • 通知は閾値を割ったときだけにし、Webhook URLは環境変数で渡す
  • 入れた直後に閾値を大きくして、自分のドメインの通知が届くか試す
  • 有効期間が短くなる2027年以降は、残り日数ではなく残りの割合で判定する

3か月のあいだ example.com を見張っていた件も、監視を入れた日に一度わざと通知を鳴らしていれば、初日で気づけた種類の失敗でした。

コードを書き終えた日ではなく、自分のドメイン名で最初の通知が届いた日を、監視が動き始めた日として扱うのが安全です。

NEXT STEP
自分のドメインで opensslコマンドを1回実行し、notAfter の日付を確かめる。

期限と発行元が分かれば、自動更新が何日前に走るかの見当がつき、閾値も決めやすくなります。

途中経過は、また書きます。

NEXT READ本棚から1本

AIで開発を組み直すなら、まずこの5冊から

入門・実践・設計まで、Claude Codeの本をレベル別に5冊選んで比べました。

→ おすすめ5冊を見る

  • この記事を書いた人
  • 最新記事

トム

SESで約8年、環境を変えられないもどかしさの中にいました。32歳で自社開発企業に転職し、今はClaude Codeと自動化で、仕事も時間も組み直している途中です。
・Javaバックエンドエンジニア(Java歴12年以上)
・資格:基本情報技術者/応用情報技術者/Java Silver/Python3エンジニア認定基礎
詳細なプロフィール

𝕏

Python