---
title: "แจ้งเตือนใบรับรอง SSL ใกล้หมดอายุ: ไม่มีใครส่งอีเมลบอกคุณอีกแล้ว"
description: "Let's Encrypt เลิกส่งอีเมลแจ้งหมดอายุ และใบรับรองเหลืออายุ 200 วัน นี่คือสคริปต์ openssl ที่ทดสอบแล้ว เกณฑ์ที่ถูกต้อง และการแจ้งเตือนที่ถึงตัวจริง"
date: 2026-09-18
author: Nooc
authorAvatarLink: /images/avatars/nooc.webp
authorLink: https://nooc.me
tags:
  - ใบรับรอง SSL หมดอายุ
  - การมอนิเตอร์ TLS
  - certbot
  - แจ้งเตือนเซิร์ฟเวอร์ล่ม
  - แจ้งเตือนด้วยสายโทร
---

# แจ้งเตือนใบรับรอง SSL ใกล้หมดอายุ: ไม่มีใครส่งอีเมลบอกคุณอีกแล้ว

มีสองอย่างที่เปลี่ยนไปใต้ระบบใบรับรองของทุกคน และทีมส่วนใหญ่ยังไม่ได้ปรับตัวกับทั้งสองอย่าง

**อย่างแรก ตาข่ายนิรภัยถูกถอดออกไปแล้ว** Let's Encrypt ปิดบริการแจ้งเตือนหมดอายุเมื่อ **4 มิถุนายน 2025** — อีเมลที่คอยช่วยเว็บนับพันไว้อย่างเงียบ ๆ ทุกครั้งที่ระบบอัตโนมัติพัง เหตุผลนั้นฟังขึ้น (ผู้ใช้ส่วนใหญ่ต่ออายุอัตโนมัติอยู่แล้ว การเก็บอีเมลนับล้านเป็นภาระด้านความเป็นส่วนตัว และบริการนี้ใช้เงิน "หลายหมื่นดอลลาร์ต่อปี") และประกาศนั้นปิดท้ายด้วยการบอกให้ไปหาบริการมอนิเตอร์จากที่อื่นแทน ([Let's Encrypt](https://letsencrypt.org/2025/06/26/expiration-notification-service-has-ended)) หลายคนอ่านถึงตรงนั้น พยักหน้าเห็นด้วย แล้วก็ไม่เคยทำครึ่งหลัง

**อย่างที่สอง ระยะเผื่อพังทลาย** ตั้งแต่ **15 มีนาคม 2026** ใบรับรอง TLS สาธารณะมีอายุได้สูงสุด 200 วัน ลดเหลือ 100 วันในวันที่ 15 มีนาคม 2027 และ 47 วันในวันที่ 15 มีนาคม 2029 ตาม [มติ SC-081v3 ของ CA/Browser Forum](https://cabforum.org/2025/04/11/ballot-sc081v3-introduce-schedule-of-reducing-validity-and-data-reuse-periods/) ส่วน Let's Encrypt เดินเร็วกว่าเพดานนั้น ใบรับรองอายุ 6 วันเปิดให้ใช้ทั่วไปตั้งแต่ 15 มกราคม 2026 และแผนจะพาโปรไฟล์ค่าเริ่มต้นลงไปที่ 64 วันในเดือนกุมภาพันธ์ 2027 และ 45 วันในเดือนกุมภาพันธ์ 2028 ([Let's Encrypt](https://letsencrypt.org/2025/12/02/from-90-to-45))

ทั้งสองอย่างดันไปทางเดียวกัน การต่ออายุเกิดถี่ขึ้น จึงมีโอกาสพังมากขึ้น และเมื่อพังก็ไม่มีใครส่งจดหมายมาบอก คู่มือนี้คือการตรวจสอบที่ปิดช่องว่างนั้น: สคริปต์เชลล์ที่ทดสอบจริงแล้ว เกณฑ์ที่สมเหตุสมผลสำหรับใบรับรองอายุสั้น และวิธีทำให้กรณีสุดท้ายโทรเข้ามือถือคุณด้วย [Echobell](https://apps.apple.com/app/apple-store/id6743597198?pt=128151925&ct=blog-ssl-certificate-expiry-alerts-th&mt=8)

## เหตุขัดข้องจากใบรับรองร้ายแรงแค่ไหนจริง ๆ

**ร้ายแรงพอที่ปีที่แล้วองค์กรมากกว่าหนึ่งในสามเจอกับตัว** ในรายงาน 2026 Global Certificate Management Outlook ของ DigiCert — สำรวจโดย Propeller Insights เมื่อพฤษภาคม 2026 กับผู้มีอำนาจตัดสินใจด้านไอทีและความมั่นคงไซเบอร์ 1,001 คนในสหรัฐฯ สหราชอาณาจักร และออสเตรเลีย — องค์กร**มากกว่าหนึ่งในสาม**รายงานว่ามีบริการหยุดทำงานเพราะใบรับรองหมดอายุในรอบปีที่ผ่านมา เกือบ**สามในสี่**สะสมเวลาดาวน์ที่เกี่ยวกับใบรับรองอย่างน้อยห้าชั่วโมง **หนึ่งในห้า**ถึง 25 ชั่วโมงขึ้นไป และเกือบ**หนึ่งในสี่**บอกว่าเหตุการณ์ใบรับรองที่หนักที่สุดสร้างความเสียหายเกิน **250,000 ดอลลาร์** ([DigiCert](https://www.globenewswire.com/news-release/2026/09/09/3358724/0/en/digicert-research-finds-certificate-failures-are-a-six-figure-infrastructure-risk.html))

ส่วนที่น่าสนใจของตัวเลขเหล่านี้คือระยะเวลา ห้าชั่วโมงไม่ใช่เวลาที่ใช้ต่ออายุใบรับรอง — การต่ออายุใช้เวลาไม่กี่วินาที ห้าชั่วโมงคือเวลาที่ใครสักคนใช้กว่าจะ*รู้ตัว*

## ทำไมการต่ออายุอัตโนมัติจึงยังไม่พอ

**เพราะระบบต่ออายุอัตโนมัติล้มเหลวอย่างเงียบ ๆ และ cron ที่หยุดทำงานไปแล้วก็ไม่มีเอาต์พุตอะไรออกมาเลย** แต่ละข้อต่อไปนี้เกิดขึ้นจริงและเกิดบ่อย และไม่มีข้อไหนส่งเสียง:

- **ตัวตั้งเวลาไม่ทำงานแล้ว** การอัปเกรดดิสโทร การสร้างคอนเทนเนอร์ใหม่ หรือคำสั่ง `systemctl disable` เมื่อสามเดือนก่อนที่ไม่มีใครจำได้ `certbot.timer` ที่ไม่ยิงหน้าตาเหมือนกับ `certbot.timer` ที่ยิงสำเร็จทุกประการ
- **ต่ออายุสำเร็จ แต่บริการไม่เคยรีโหลด** ใบรับรองใหม่อยู่บนดิสก์แล้ว แต่ nginx, HAProxy หรือ Postfix ยังถือใบเก่าไว้ในหน่วยความจำ นี่คือทางที่การตั้งค่าแบบ "อัตโนมัติเต็มรูปแบบ" หมดอายุได้บ่อยที่สุด
- **เส้นทาง challenge พัง** มีคนใส่ redirect, กฎ WAF หรือ `Deny` ไว้หน้า `/.well-known/acme-challenge/` ทำให้ HTTP-01 ล้มเหลว หรือโทเคน API ของผู้ให้บริการ DNS ที่ใช้กับ DNS-01 หมดอายุ
- **ต่ออายุเกิดขึ้นแค่โหนดเดียว** โหลดบาลานเซอร์สองตัว cron ตัวเดียว ตัวที่สองจะเสิร์ฟใบเก่าต่อไปจนกว่าจะเสิร์ฟไม่ได้อีก
- **ใบรับรองนั้นไม่ได้อยู่บนเว็บเซิร์ฟเวอร์เลย** ไคลเอนต์ mTLS ภายใน, Kafka broker, เซิร์ฟเวอร์ LDAP, อุปกรณ์ VPN, ใบรับรอง push ของระบบจัดการอุปกรณ์ ไม่มีอะไรบนอินเทอร์เน็ตสาธารณะมองเห็นมัน และไม่มีไคลเอนต์ ACME ตัวไหนดูแลมัน
- **การต่ออายุถูกฮาร์ดโค้ดไว้ที่ช่วงเวลาที่ผิด** Let's Encrypt พูดตรง ๆ ว่า เมื่อโปรไฟล์ค่าเริ่มต้นลงไปที่ 64 แล้วเป็น 45 วัน "การต่ออายุด้วยช่วงเวลาตายตัว 60 วันจะไม่เพียงพออีกต่อไป" ([Let's Encrypt](https://letsencrypt.org/2025/12/02/from-90-to-45))

ข้อสุดท้ายควรเน้น เพราะนี่คือความล้มเหลวที่ปฏิทินกำลังผลักทุกคนเข้าไปหา ถ้าจังหวะการต่ออายุของคุณคือตัวเลขที่ใครบางคนพิมพ์ไว้เมื่อปี 2022 ตอนนี้อายุใบรับรองกำลังเดินเข้าหาตัวเลขนั้นเอง

## จะตรวจวันหมดอายุของใบรับรองจากบรรทัดคำสั่งอย่างไร

**ไปป์ไลน์ `openssl` เส้นเดียว ไม่ต้องติดตั้งอะไรเพิ่ม** สำหรับโฮสต์ที่รันอยู่:

```bash
openssl s_client -connect example.com:443 -servername example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -subject -issuer -startdate -enddate
```

สำหรับไฟล์บนดิสก์:

```bash
openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -noout -enddate
```

บน IP ที่ใช้ร่วมกัน แฟล็ก `-servername` ไม่ใช่ตัวเลือกเสริม — ถ้าไม่มี SNI คุณจะได้ใบรับรองที่เซิร์ฟเวอร์ถือเป็นค่าเริ่มต้น ซึ่งอาจไม่ใช่ใบที่คุณกังวล

ถ้าต้องการแค่คำตอบว่าใช่หรือไม่ ข้ามการแปลงวันที่ไปได้เลย `openssl x509 -checkend <วินาที>` จะออกด้วยรหัส **0** ถ้าใบรับรองอยู่รอดพ้นช่วงนั้น และ **1** ถ้าหมดอายุภายในช่วงนั้น (รวมถึงกรณีที่หมดอายุไปแล้ว):

```bash
openssl x509 -in fullchain.pem -noout -checkend $((14 * 86400)) \
  || echo "หมดอายุภายใน 14 วัน"
```

ข้อตกลงเรื่องรหัสออกนี้แหละคือแก่นของการมอนิเตอร์ทั้งหมด ทุกอย่างที่เหลือเป็นแค่ท่อรอบ ๆ มัน

### จับกรณี "ต่ออายุแล้วแต่ยังไม่รีโหลด"

**เทียบใบที่อยู่บนดิสก์กับใบที่ถูกเสิร์ฟจริง** นี่คือการตรวจที่แทบไม่มีใครทำ และมันจับความล้มเหลวที่ระบบต่ออายุอัตโนมัติมองไม่เห็นได้พอดี:

```bash
served=$(openssl s_client -connect 127.0.0.1:443 -servername example.com </dev/null 2>/dev/null \
         | openssl x509 -noout -fingerprint -sha256)
ondisk=$(openssl x509 -in /etc/letsencrypt/live/example.com/fullchain.pem -noout -fingerprint -sha256)

[ "$served" = "$ondisk" ] || echo "บริการยังเสิร์ฟใบรับรองเก่า — ต้องรีโหลด"
```

ทั้งสองคำสั่งพิมพ์รูปแบบเดียวกันคือ `sha256 Fingerprint=AB:CD:...` ดังนั้นเทียบสตริงตรง ๆ ก็พอ ให้รันหลังจากช่วงเวลาที่ตัวตั้งเวลาต่ออายุทำงานสักไม่กี่นาที

## การแจ้งเตือนใบรับรองควรใช้เกณฑ์อะไร

**ใช้สัดส่วนของอายุใบรับรอง ไม่ใช่จำนวนวันตายตัว** กฎ "เตือนล่วงหน้า 30 วัน" เคยสมเหตุสมผลกับใบรับรอง 90 วัน พอมาใช้กับใบ 47 วันมันจะเตือนทั้งที่ใบยังสมบูรณ์ดี และถ้าใช้กับใบ 6 วันมันจะเตือนตลอดเวลา

ให้ยึดบันไดขั้นไว้กับจุดต่ออายุแทน Let's Encrypt แนะนำให้ต่ออายุ "ประมาณสองในสามของอายุใบรับรองปัจจุบัน" ดังนั้น **เหลืออายุหนึ่งในสาม** คือจุดที่การต่ออายุ*ควรจะเกิดขึ้นไปแล้ว* ทุกอย่างหลังจุดนั้นคือหลักฐานว่ามันไม่ได้เกิด:

| อายุที่เหลือ | หมายความว่าอย่างไร | ประเภทการแจ้งเตือน |
| --- | --- | --- |
| 1/3 | หน้าต่างการต่ออายุเปิดแล้ว | ไม่ต้องแจ้ง — นี่คือปกติ |
| 1/6 | พลาดหน้าต่างไปหนึ่งครั้ง | Push ปกติ |
| 1/12 | การต่ออายุกำลังล้มเหลว ไม่ใช่แค่ช้า | เร่งด่วน |
| น้อยกว่า 1/24, หมดอายุ หรือติดต่อไม่ได้ | เหลืออีกไม่กี่ชั่วโมงก็ล่ม | สายโทรเข้า |

แปลงเป็นตัวเลขจริง สำหรับใบรับรอง 47 วันจะประมาณนี้: เงียบจนถึง 7.8 วัน, push ที่ 3.9 วัน, เร่งด่วนที่ 2 วัน, โทรเมื่อเหลือไม่ถึง 1 วัน สำหรับใบ 90 วัน: 15 วัน, 7.5 วัน, 3.75 วัน ส่วนใบ 6 วันนับกันเป็นชั่วโมง และตรงนั้นบันไดสำหรับมนุษย์ไม่ใช่เครื่องมือที่เหมาะอีกต่อไป — ให้พึ่ง [ACME Renewal Information](https://letsencrypt.org/2025/09/16/ari-rfc) (ARI เผยแพร่เป็น RFC 9773) ซึ่งให้ CA บอกไคลเอนต์เองว่าควรต่ออายุเมื่อไร แล้วแจ้งเตือนเฉพาะตอนที่ไคลเอนต์ล้มเหลวซ้ำ ๆ

การคำนวณสัดส่วนใช้แค่สี่บรรทัด และทำให้สคริปต์เดียวกันถูกต้องกับทุกใบรับรองที่คุณมี ไม่ว่าใครเป็นผู้ออก:

```bash
to_epoch() {
  date -u -d "$1" +%s 2>/dev/null || date -u -j -f '%b %d %T %Y %Z' "$1" +%s 2>/dev/null
}

pct_left() {  # อ่าน PEM จาก stdin แล้วพิมพ์เปอร์เซ็นต์ของอายุที่เหลือ
  local pem nb na
  pem=$(cat)
  nb=$(to_epoch "$(printf '%s' "$pem" | openssl x509 -noout -startdate | cut -d= -f2)")
  na=$(to_epoch "$(printf '%s' "$pem" | openssl x509 -noout -enddate | cut -d= -f2)")
  echo $(( (na - $(date -u +%s)) * 100 / (na - nb) ))
}
```

รูปแบบ `date` แรกเป็นของ GNU ส่วนอันที่สองเป็นของ BSD/macOS เครื่องหมาย `||` จะเลือกอันที่เครื่องคุณมี

## จะเปลี่ยนสิ่งนี้ให้เป็นการแจ้งเตือนที่ถึงตัวจริง ๆ ได้อย่างไร

Echobell เปลี่ยน webhook หรืออีเมลให้กลายเป็น push ปกติ การแจ้งเตือนเร่งด่วน หรือสายโทรเข้าจริง ๆ ที่ดังทะลุโหมดโฟกัสและห้ามรบกวน (ดู [ทะลุโหมดโฟกัสของ iOS](/blog/how-to-bypass-ios-focus-mode-for-critical-alerts)) สำหรับใบรับรอง เรื่องนี้สำคัญ เพราะขั้นสุดท้ายในตารางข้างบนคือขั้นที่คุณจะไปเจอตอนตีสามของวันอาทิตย์

### ขั้นที่ 1 — สร้างสองช่อง ไม่ใช่ช่องเดียว

สร้างช่องในแอป ตั้งประเภทการแจ้งเตือนเป็น **เร่งด่วน** และตั้งชื่อว่า "ใบรับรองใกล้หมดอายุ" แล้วสร้างช่องที่สองเป็นแบบ **สายโทรเข้า** ตั้งชื่อว่า "ใบรับรองจวนหมดอายุ" คัดลอก URL ของ webhook จากรายละเอียดของแต่ละช่อง หน้าตาจะเป็น `https://hook.echobell.one/t/<channel-token>` ให้ถือว่าเป็นความลับ — ใครถือ URL ของช่องสายโทรเข้าก็ทำให้มือถือคุณดังได้ ([คู่มือ webhook](/docs/webhook))

เขียนเทมเพลตให้อ่านจากหน้าจอล็อกแล้วลงมือได้เลยโดยไม่ต้องปลดล็อกอะไร:

```
หัวข้อ: TLS {{state}}: {{host}}
เนื้อหา: เหลือ {{daysLeft}} วัน หมดอายุ {{notAfter}} — ออกโดย {{issuer}}
```

คีย์ JSON ทุกตัวที่คุณส่งไปจะกลายเป็นตัวแปร ([เทมเพลต](/docs/template))

### ขั้นที่ 2 — รันการตรวจสอบ

นี่คือสคริปต์จากหัวข้อก่อนหน้า ประกอบรวมและทดสอบตั้งแต่ต้นจนจบแล้ว บันทึกเป็น `/usr/local/bin/cert-watch`:

```bash
#!/usr/bin/env bash
# cert-watch — ส่ง POST ไปยัง Echobell เมื่อใบรับรอง TLS ใกล้หมดอายุ
set -uo pipefail

HOOK="${ECHOBELL_CERT_HOOK:?กำหนด ECHOBELL_CERT_HOOK เป็น URL webhook ของช่องคุณ}"
WARN_DAYS="${WARN_DAYS:-14}"

post() {
  curl -sS -m 10 -X POST "$HOOK" \
    -H 'content-type: application/json' \
    -d "{\"host\":\"$1\",\"daysLeft\":$2,\"notAfter\":\"$3\",\"state\":\"$4\"}" \
    >/dev/null
}

days_left() {
  local end
  end=$(date -u -d "$1" +%s 2>/dev/null) ||
    end=$(date -u -j -f '%b %d %T %Y %Z' "$1" +%s 2>/dev/null) || return 1
  echo $(( (end - $(date -u +%s)) / 86400 ))
}

check() {
  local host="$1" port="$2" pem state not_after days

  pem=$(openssl s_client -connect "$host:$port" -servername "$host" \
        </dev/null 2>/dev/null | openssl x509 2>/dev/null)

  if [ -z "$pem" ]; then
    post "$host" 0 "" "unreachable"
    return
  fi

  if printf '%s' "$pem" | openssl x509 -noout -checkend 0 >/dev/null 2>&1; then
    printf '%s' "$pem" | openssl x509 -noout -checkend $((WARN_DAYS * 86400)) >/dev/null 2>&1 && return 0
    state="expiring"
  else
    state="expired"
  fi

  not_after=$(printf '%s' "$pem" | openssl x509 -noout -enddate | cut -d= -f2)
  days=$(days_left "$not_after") || days=-999
  post "$host" "$days" "$not_after" "$state"
}

for target in "$@"; do
  case "$target" in
    *:*) check "${target%:*}" "${target##*:}" ;;
    *) check "$target" 443 ;;
  esac
done
```

มันรายงานสามสถานะ — `expiring`, `expired` และ `unreachable` — และเงียบเมื่อทุกอย่างปกติ เป้าหมายเขียนเป็น `host` หรือ `host:port` ได้ ใบรับรองที่ไม่ใช่เว็บจึงครอบคลุมด้วย:

```bash
ECHOBELL_CERT_HOOK="https://hook.echobell.one/t/<token>" \
  cert-watch example.com api.example.com mail.example.com:993 ldap.internal:636
```

`unreachable` ถูกตั้งใจให้เป็นการแจ้งเตือน ไม่ใช่การข้ามไปเงียบ ๆ การตรวจที่ถือว่า "ฉันดูไม่ได้" เท่ากับ "ทุกอย่างปกติ" คือต้นเหตุที่ทำให้ใบรับรองหมดอายุตั้งแต่แรก

### ขั้นที่ 3 — ตั้งเวลา และแจ้งเตือนเมื่อตัวตั้งเวลาเองล้มเหลว

วันละครั้งพอสำหรับใบรับรอง 45 วันขึ้นไป วันละสองครั้งถ้าคุณใช้ใบ 6 วัน ตัวอย่าง systemd timer:

```ini
# /etc/systemd/system/cert-watch.service
[Unit]
Description=TLS certificate expiry check

[Service]
Type=oneshot
EnvironmentFile=/etc/echobell.env
ExecStart=/usr/local/bin/cert-watch example.com api.example.com mail.example.com:993
```

```ini
# /etc/systemd/system/cert-watch.timer
[Unit]
Description=Daily TLS certificate expiry check

[Timer]
OnCalendar=daily
RandomizedDelaySec=1h
Persistent=true

[Install]
WantedBy=timers.target
```

`Persistent=true` สำคัญ: ถ้าไม่มีมัน เครื่องที่ปิดอยู่ตอนถึงเวลาที่กำหนดจะข้ามรอบนั้นไปเฉย ๆ

จากนั้นปิดวงจรที่ตัวการตรวจสอบเอง ยูนิตแบบ `Type=oneshot` ที่ออกด้วยรหัสไม่เป็นศูนย์จะทริกเกอร์ `OnFailure=` ดังนั้น drop-in เดียวก็ทำให้การต่ออายุที่พังประกาศตัวเองได้:

```ini
# /etc/systemd/system/certbot.service.d/echobell.conf
[Unit]
OnFailure=echobell-alert@%n.service
```

```ini
# /etc/systemd/system/echobell-alert@.service
[Unit]
Description=Echobell alert for %i

[Service]
Type=oneshot
EnvironmentFile=/etc/echobell.env
ExecStart=/usr/local/bin/echobell-notify %i
```

โดย `/usr/local/bin/echobell-notify` มีแค่สามบรรทัด:

```bash
#!/usr/bin/env bash
curl -sS -m 10 -X POST "$ECHOBELL_CERT_HOOK" \
  -H 'content-type: application/json' \
  -d "{\"host\":\"$(hostname -f)\",\"state\":\"renewal-failed\",\"unit\":\"$1\",\"daysLeft\":-1}"
```

`certbot renew` จะออกด้วยรหัสไม่เป็นศูนย์เมื่อมีการต่ออายุใดล้มเหลว ซึ่งเป็นสัญญาณที่คุณต้องการพอดี และเป็นสัญญาณที่ตอนนี้ไม่ได้ส่งไปไหนเลยพอดีเช่นกัน โปรดทราบว่า `--deploy-hook` ของ certbot ทำงานเฉพาะเมื่อ*สำเร็จ* จึงใช้กับเรื่องนี้ไม่ได้ — เส้นทางความล้มเหลวต้องมาจากฝั่งยูนิต

### ขั้นที่ 4 — แยกบันไดขั้นด้วยเงื่อนไข

ทั้งสองช่องรับ payload เดียวกัน ส่วนช่องไหนจะทำงานจริงนั้น [เงื่อนไข](/docs/conditions) เป็นตัวตัดสิน ที่ช่องเร่งด่วน:

```
state == "expiring" && daysLeft > 3
```

ที่ช่องสายโทรเข้า:

```
state == "expired" || state == "unreachable" || state == "renewal-failed" || daysLeft <= 3
```

สังเกตว่า `<=` จะแปลงทั้งสองฝั่งด้วย `Number()` ดังนั้นแม้ `daysLeft` จะถูกส่งมาเป็นสตริงก็ยังเทียบกันแบบตัวเลข เงื่อนไขไม่มีตัวดำเนินการ "มีคำว่า" ซึ่งเป็นเหตุผลที่สคริปต์ส่งฟิลด์ `state` มาอย่างชัดเจน แทนที่จะเป็นข้อความอิสระที่คุณต้องมาจับรูปแบบเอง

### ขั้นที่ 5 — ทดสอบก่อนจะไว้ใจ

ชี้สคริปต์ไปที่โฮสต์ที่รู้อยู่แล้วว่าใบรับรองมีปัญหา แล้วดูว่าการแจ้งเตือนมาถึงไหม:

```bash
ECHOBELL_CERT_HOOK="https://hook.echobell.one/t/<token>" cert-watch expired.badssl.com
```

ทำแบบนี้โดยเปิดโหมดห้ามรบกวนบนเครื่องที่จะรับจริง และเปิด **โทรซ้ำเมื่อสายไม่สำเร็จ** ในแอป เพื่อให้สายที่ถูกโหมดโฟกัสกดไว้ถูกโทรอีกครั้ง เส้นทางยกระดับที่ไม่เคยยิงทดสอบเลยก็เป็นแค่การเดา

## ถ้าระบบมอนิเตอร์ของฉันตรวจใบรับรองอยู่แล้วล่ะ

**ก็ต่อ webhook ที่มีอยู่แล้วเข้ากับช่อง แล้วข้ามสคริปต์ไปได้เลย** ระบบมอนิเตอร์ส่วนใหญ่รู้วันหมดอายุอยู่แล้ว สิ่งที่มักขาดคือเส้นทางที่ผ่านคนที่กำลังหลับไปได้

- **Uptime Kuma** มีการแจ้งเตือนใบรับรองหมดอายุในตัวอยู่แล้ว — ชี้ไปที่ช่องได้เลย ([คู่มือ Uptime Kuma](/docs/developer/uptime-kuma), [ตั้งค่าสายโทรเข้า](/blog/uptime-kuma-phone-call-alerts))
- **Prometheus + Alertmanager** คู่กับ blackbox exporter จะให้ `probe_ssl_earliest_cert_expiry` ตั้งกฎบนเมตริกนั้นแล้วส่งไปยังช่อง ([คู่มือ Prometheus](/docs/developer/prometheus), [สายโทรเข้าผ่าน Alertmanager](/blog/alertmanager-phone-call-alerts))
- กฎแจ้งเตือนของ **Grafana** ส่ง POST ตรงไปยังช่องได้ ([คู่มือ Grafana](/docs/developer/grafana))
- **Upptime** และ **UptimeRobot** ครอบคลุมเอนด์พอยต์สาธารณะ ([Upptime](/docs/developer/upptime), [UptimeRobot](/docs/developer/uptimerobot))
- **อะไรก็ตามที่ส่งได้แค่อีเมล** — พอร์ทัลของ CA เอง การแจ้งเตือน ACM ของผู้ให้บริการคลาวด์ หรือ PKI ภายใน — ใช้กฎการส่งต่ออีเมลแทน แต่ละช่องมีที่อยู่อีเมลของตัวเอง และใช้ `from`, `to`, `subject`, `text`, `html` เป็นตัวแปรได้ ([ทริกเกอร์อีเมล](/docs/email-trigger))

สิ่งเดียวที่ไม่มีตัวเลือกไหนทดแทนได้คือการให้การตรวจสอบรันจาก*นอก*เครื่องที่เสิร์ฟใบรับรองนั้น ถ้าระบบมอนิเตอร์อยู่บนโฮสต์เดียวกัน เหตุขัดข้องที่ทำให้โฮสต์ล่มก็จะพาการแจ้งเตือนล่มไปด้วย

## สิ่งที่ Echobell ไม่ทำ

ตรงนี้ต้องพูดให้ชัด เพราะการจัดการใบรับรองเป็นหมวดที่เต็มไปด้วยผลิตภัณฑ์ที่ทำได้มากกว่านี้เยอะ

**Echobell ทำ:** เปลี่ยน webhook หรืออีเมลให้เป็น push ปกติ การแจ้งเตือนเร่งด่วน หรือสายโทรเข้าที่ดังจริง กรองด้วยเงื่อนไข จัดรูปแบบด้วยเทมเพลต และส่งทริกเกอร์เดียวไปถึงผู้ติดตามทุกคนของช่องที่แชร์ไว้ โดยแต่ละคนเลือกระดับความเร่งด่วนของตัวเอง

**Echobell ไม่ทำ:**

- **ค้นหาหรือทำบัญชีใบรับรองให้คุณ** มันไม่สแกนเครือข่าย ไม่ไล่อ่านล็อก certificate transparency และจะไม่บอกคุณเรื่องใบรับรองที่ไม่มีใครจำได้ว่าเคยออก สคริปต์ข้างบนตรวจเฉพาะโฮสต์ที่คุณระบุไว้เท่านั้น การมีใบรับรองกระจัดกระจายเป็นปัญหาจริง และนี่ไม่ใช่ทางแก้ของมัน
- **ต่ออายุอะไรให้** ไม่มีไคลเอนต์ ACME และไม่แตะกุญแจของคุณ มันบอกแค่ว่าการต่ออายุพัง ส่วนการซ่อมยังเป็นงานของคุณ
- **ตรวจใบรับรองตามตารางของตัวเอง** ไม่มีโพรบแบบโฮสต์ให้ ต้องมีบางอย่างที่คุณรันเอง — ตัวตั้งเวลา งาน CI หรือระบบมอนิเตอร์ที่มีอยู่ — เป็นคนไปดู
- **จัดเวรรับสาย นโยบายยกระดับ หรือการตอบรับ** ไม่มี "ถ้าห้านาทีไม่มีใครรับ ให้โทรหาคนถัดไป" ถ้าต้องการแบบนั้น คุณต้องใช้แพลตฟอร์มจัดการเหตุการณ์ — ดู [เปรียบเทียบทางเลือกแทน Opsgenie](/blog/opsgenie-end-of-life-alternatives)
- **รับประกันการส่งถึง** สายโทรขึ้นอยู่กับโครงสร้างพื้นฐาน push เครือข่าย และมือถือที่มีแบตเตอรี่ มันย่นระยะระหว่างตอนที่พังกับตอนที่รู้ ไม่ใช่มาตรการควบคุมที่พึ่งพาได้แบบเด็ดขาด

## คำถามที่พบบ่อย

### Let's Encrypt เลิกส่งอีเมลแจ้งหมดอายุจริงหรือ

จริง บริการแจ้งเตือนหมดอายุสิ้นสุดเมื่อ 4 มิถุนายน 2025 และ Let's Encrypt ได้ลบที่อยู่อีเมลที่เคยเก็บไว้คู่กับบันทึกการออกใบรับรองทิ้งด้วย ประกาศนั้นแนะนำให้ใช้บริการมอนิเตอร์จากภายนอก และยก Red Sift Certificates Lite ที่ฟรีถึง 250 ใบเป็นหนึ่งในตัวเลือก ถ้าคุณไม่ได้รับคำเตือนหมดอายุจาก Let's Encrypt มากว่าหนึ่งปีแล้ว นี่คือเหตุผล — ไม่ใช่เพราะไม่เคยมีใบไหนใกล้หมดอายุเลย

### ปี 2026 ใบรับรอง TLS มีอายุนานแค่ไหน

สูงสุด 200 วันสำหรับใบรับรอง TLS ที่เชื่อถือได้สาธารณะ นับตั้งแต่ 15 มีนาคม 2026 เพดานจะลดเหลือ 100 วันในวันที่ 15 มีนาคม 2027 และ 47 วันในวันที่ 15 มีนาคม 2029 ตามมติ SC-081v3 แต่ละ CA จะออกต่ำกว่าเพดานเพื่อความปลอดภัย เช่น DigiCert ออกใบอายุ 199 วันโดยระบุชัดว่า "เพื่อเลี่ยงการเกินอายุสูงสุดที่อนุญาต" ส่วน Let's Encrypt ไปไกลและเร็วกว่านั้นตามตารางของตัวเอง โดยใบรับรอง 6 วันเปิดใช้ทั่วไปตั้งแต่มกราคม 2026

### ถ้าใช้ ARI แล้วยังต้องแจ้งเตือนหมดอายุอีกไหม

ยังต้อง แต่เปลี่ยนเหตุการณ์ที่จับ ARI (RFC 9773) บอกไคลเอนต์ ACME ว่าควรต่ออายุ*เมื่อไร* ซึ่งกำจัดปัญหาช่วงเวลาฮาร์ดโค้ดไปได้หมด แต่ไม่รับประกันว่าการต่ออายุจะสำเร็จ ว่าบริการจะรีโหลด หรือว่าไคลเอนต์ยังทำงานอยู่ ให้แจ้งเตือนเมื่อการต่ออายุล้มเหลวซ้ำ ๆ และเมื่อใบที่เสิร์ฟอยู่ไม่ตรงกับใบบนดิสก์ แทนที่จะแจ้งเตือนจากการนับถอยหลังที่คุณไม่ต้องจัดการเองแล้ว

### แล้วใบรับรองที่ไม่ได้อยู่บนเว็บเซิร์ฟเวอร์ล่ะ

พวกนั้นแหละที่หมดอายุบ่อยที่สุด เพราะไม่มีไคลเอนต์ ACME คอยดู และไม่มีเบราว์เซอร์ตัวไหนบ่นจนกว่าจะมีอะไรพัง สคริปต์รับ `host:port` ดังนั้น IMAP ที่ 993, LDAPS ที่ 636, Kafka broker ที่ 9093 หรือ API ภายในที่ 8443 ใช้ได้เหมือนกันหมด ส่วนใบรับรองที่ไม่เคยแตะซ็อกเก็ตเลย — เซ็นโค้ด ใบรับรองแจ้งเตือน push ใบรับรองไคลเอนต์ในฝูงอุปกรณ์ — ต้องดึงวันที่จากที่ที่มันอยู่ แล้วส่งไปยังช่องเดียวกัน

### ตรวจทุกวันจะกลายเป็นเสียงรบกวนไหม

ไม่ ถ้ามันเงียบตอนที่ไม่มีอะไรผิดปกติ ซึ่งเป็นเหตุผลที่สคริปต์ไม่ส่งอะไรเลยเมื่ออยู่เหนือเกณฑ์ แบบที่รบกวนจริง ๆ คือแบบที่รายงาน "ใบรับรองปกติดี" ทุกวัน — สองสัปดาห์ผ่านไปไม่มีใครอ่าน และวันที่มันหยุดมาก็ไม่มีใครสังเกต ถ้าอยากได้สัญญาณชีพ ให้ไปไว้ที่ช่องปกติแยกต่างหาก และอย่าไว้ที่ช่องที่ดังเด็ดขาด ดูเวอร์ชันทั่วไปของข้อโต้แย้งนี้ได้ที่ [แก้อาการล้าจากการแจ้งเตือน](/blog/fix-alert-fatigue-developer-guide)

### ทั้งทีมรับการแจ้งเตือนใบรับรองได้ไหม

ได้ แชร์ช่องแล้วผู้ติดตามทุกคนจะได้รับทริกเกอร์ โดยแต่ละคนเลือกประเภทการแจ้งเตือนของตัวเอง การแบ่งที่ใช้ได้จริง: คนที่เข้าเวรสมัครช่องสายโทรเข้า ที่เหลือสมัครช่องเร่งด่วน แบบนี้ใบหมดอายุตอนตีสามจะปลุกคนเดียว ไม่ใช่หกคน

### ใช้บน macOS ได้ไหม

ได้ มีข้อควรระวังอย่างเดียว: `date` ของ BSD ไม่รับ `-d` จึงเป็นเหตุผลที่ `days_left` และ `to_epoch` ลองรูปแบบ GNU ก่อนแล้วค่อยถอยไปใช้ `date -u -j -f` ส่วน `openssl` บน macOS เป็น LibreSSL โดยค่าเริ่มต้น และรองรับ `-checkend` กับ `-fingerprint` เหมือนกันทุกประการ ถ้าคุณติดตั้ง OpenSSL จาก Homebrew ก็ไม่มีอะไรเปลี่ยน

### ใส่รายละเอียดใบรับรองในการแจ้งเตือนปลอดภัยไหม

ฟิลด์ที่ใช้ตรงนี้ — ชื่อโฮสต์ วันหมดอายุ ผู้ออกใบรับรอง — เป็นข้อมูลสาธารณะ ใครก็อ่านจากเซิร์ฟเวอร์คุณได้ด้วยคำสั่ง `openssl` เดียวกัน อย่าขยาย payload ด้วยกุญแจส่วนตัว พาธภายใน หรืออะไรก็ตามจากใบรับรองบนโฮสต์ที่ไม่เปิดสาธารณะนอกเหนือจากชื่อโฮสต์ Echobell เก็บเนื้อหาและประวัติการแจ้งเตือนไว้บนเครื่องของคุณเท่านั้น ฝั่งเซิร์ฟเวอร์เก็บแค่บัญชี ช่อง และการติดตาม ([โมเดลความเป็นส่วนตัว](/docs/features)) ซึ่งเป็นค่าเริ่มต้นที่ดี แต่ไม่ใช่เหตุผลให้ส่งมากเกินจำเป็น

---

## บทความที่เกี่ยวข้อง

- [แจ้งเตือนด้วยสายโทรเมื่อ API ล่ม](/blog/phone-call-alerts-api-downtime)
- [แจ้งเตือนด้วยสายโทรจาก Uptime Kuma](/blog/uptime-kuma-phone-call-alerts)
- [แจ้งเตือนเมื่องาน cron ล้มเหลว](/blog/cron-job-failure-alerts)
- [แก้อาการล้าจากการแจ้งเตือน: คู่มือสำหรับนักพัฒนา](/blog/fix-alert-fatigue-developer-guide)
- [วิธีให้การแจ้งเตือนสำคัญทะลุโหมดโฟกัสของ iOS](/blog/how-to-bypass-ios-focus-mode-for-critical-alerts)
- [คู่มือการเชื่อมต่อ webhook](/docs/webhook)
- [คู่มือเงื่อนไข](/docs/conditions)
