Let's Encrypt ออกใบรับรองอายุเก้าสิบวันและต่ออายุให้อัตโนมัติ นั่นคือเหตุผลที่ผู้คนเลิกคิดถึงมันหลังตั้งค่าไปหนึ่งสัปดาห์ ปัญหาคือการต่ออายุอัตโนมัติพังอย่างเงียบ ๆ: ไม่มีใครเห็นข้อผิดพลาด และสามเดือนต่อมาเว็บไซต์ก็ทักทายผู้เข้าชมด้วยคำเตือนของเบราว์เซอร์ ตั้งแต่ปี 2025 Let's Encrypt เลิกส่งอีเมลแจ้งเตือนวันหมดอายุแล้วด้วย ดังนั้นการเตือนจากภายนอกอย่างสุดท้ายก็หายไปเช่นกัน

ด้านล่างคือสาเหตุที่การต่ออายุหยุดทำงาน เรียงตามความถี่จากมากไปน้อย

1. ใบรับรองต่ออายุแล้วแต่บริการไม่ได้อ่านใหม่

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

การรักษาคือ hook ที่รันหลังการต่ออายุสำเร็จ:

sudo certbot renew --deploy-hook "systemctl reload nginx"

สิ่งนี้ถูกเขียนลงไฟล์การต่ออายุครั้งเดียว (/etc/letsencrypt/renewal/example.com.conf ส่วน [renewalparams] บรรทัด renew_hook) และทำงานเองตั้งแต่นั้น

2. เว็บเซิร์ฟเวอร์เปลี่ยนแต่ challenge ไม่เปลี่ยน

HTTP-01 challenge ต้องการให้ส่งไฟล์จาก /.well-known/acme-challenge/ ผ่าน HTTP ธรรมดา มันมักพังแบบนี้:

  • มีการเพิ่มการเปลี่ยนเส้นทางจาก HTTP ไป HTTPS แบบไม่มีเงื่อนไขในไฟล์ตั้งค่า — challenge ตามการเปลี่ยนเส้นทางไปแล้วล้มเหลว;
  • มีกฎ location ~ /\. พร้อม deny all ปรากฏขึ้น ซึ่งปิดทุกไดเรกทอรีที่มีจุด รวมถึง .well-known;
  • รากของเว็บไซต์ย้ายที่แต่การตั้งค่าของ certbot ยังเก็บ --webroot-path เดิมไว้;
  • มีการเปิดการบล็อกตามประเทศหรือการยืนยันตัวตนพื้นฐานและมันจับเซิร์ฟเวอร์ตรวจสอบไปพร้อมกับคนอื่น

ลำดับที่ถูกต้องในไฟล์ตั้งค่า: location เฉพาะสำหรับ /.well-known/acme-challenge/ ก่อน แล้วจึงตามด้วยการเปลี่ยนเส้นทางทั่วไป

3. ไทเมอร์ถูกปิด

การต่ออายุรันจากไทเมอร์ของ systemd หรืองาน cron และทั้งสองอย่างอาจถูกปิดโดยบังเอิญขณะแก้จุดบกพร่องหรือหายไปตอนย้ายเซิร์ฟเวอร์

systemctl list-timers | grep certbot
sudo certbot renew --dry-run

คำสั่งที่สองรันวงจรการต่ออายุเต็มรูปแบบในโหมดทดสอบโดยไม่ใช้โควตาของคุณ นี่คือการตรวจเดียวที่ตอบคำถาม «อีกหนึ่งเดือนมันจะต่ออายุไหม» ล่วงหน้า ไม่ใช่หลังเกิดเหตุ

4. โดเมนหนึ่งไม่ได้ชี้มาที่นี่แล้ว

ใบรับรองครอบคลุมห้าโดเมน หนึ่งในนั้นย้ายไปโฮสต์อื่น DNS เปลี่ยนไป การตรวจสอบของโดเมนนั้นจึงไม่ผ่าน และ certbot ก็ไม่ต่ออายุใบรับรองทั้งใบ — รวมถึงสี่โดเมนที่ยังใช้งานได้ ต้องเอาโดเมนส่วนเกินออกจากใบรับรอง ไม่ใช่เพิกเฉยไปพร้อมกับข้อความ

5. คุณชนขีดจำกัด

Let's Encrypt จำกัดจำนวนใบรับรองต่อโดเมนต่อสัปดาห์ ปกติแล้วนั่นเป็นผลของวงจร «ไม่ได้ผล ลองอีกครั้ง» ขณะแก้จุดบกพร่อง มีเซิร์ฟเวอร์ staging สำหรับการทดลอง (--dry-run หรือ --test-cert) และควรใช้มันก่อนที่โควตาจะหมด ไม่ใช่หลังจากนั้น

ตรวจสิ่งที่ถูกส่งออกไป ไม่ใช่สิ่งที่เก็บไว้

การตรวจทุกแบบที่อิงไฟล์บนดิสก์มีข้อบกพร่องหนึ่ง: ไฟล์อาจใหม่ในขณะที่ผู้เข้าชมได้รับใบรับรองเก่า ดูจากฝั่งไคลเอนต์:

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

ตัวเลือก -servername เป็นสิ่งบังคับ: ถ้าไม่มีมัน บนเซิร์ฟเวอร์ที่มีหลายเว็บไซต์คุณจะได้ใบรับรองของ virtual host ที่มาก่อน ไม่ใช่ของที่คุณหมายถึง ในผลลัพธ์ notAfter คือวันหมดอายุจริง

ในโอกาสเดียวกันให้ตรวจห่วงโซ่ด้วย:

echo | openssl s_client -connect example.com:443 -servername example.com -showcerts 2>/dev/null | grep -c 'BEGIN CERTIFICATE'

ถ้ามีใบรับรองเพียงใบเดียว แสดงว่าใบกลางไม่ได้ถูกส่งไป เบราว์เซอร์มักให้อภัยการจัดวางแบบนี้ — พวกมันสร้างห่วงโซ่เอง — แต่ curl แอปพลิเคชันมือถือ และไคลเอนต์ Java จะตรวจสอบไม่ผ่าน นี่คือคลาสสิก «ที่ผมเปิดได้ปกติแต่ลูกค้าใช้ไม่ได้»

เตือนล่วงหน้ากี่วัน

สามสิบวันเป็นการเตือนครั้งแรกที่สมเหตุสมผล: นั่นคือเวลาที่เหลือตอนที่ certbot เริ่มพยายามต่ออายุ ดังนั้นถ้าถึงตอนนั้นยังไม่มีอะไรเกิดขึ้น แสดงว่ากลไกพัง สิบสี่วันคือการเตือนครั้งที่สอง ซึ่งต้องลงมือวันนี้แล้ว เจ็ดวันคือภาวะฉุกเฉิน

สิ่งสำคัญคือการดูใบรับรองทุกใบพร้อมกัน รวมถึงใบที่ไม่ได้มาจาก Let's Encrypt: ใบที่ซื้อไว้หนึ่งปีถูกลืมได้อย่างน่าเชื่อถือที่สุด เพราะในหนึ่งปีทุกคนลืมทั้งว่ามันมีอยู่และว่าการแจ้งเตือนไปเข้ากล่องจดหมายของใคร หน้าเดียวที่มีโดเมน วันที่ และจำนวนวันที่เหลือปิดคำถามนี้ได้ทั้งหมด — อย่างในเดโมด้านล่าง