Chứng chỉ TLS hết hạn: nguyên nhân và cách phòng
Chứng chỉ hết hạn là sự cố rất phổ biến nhưng phòng được gần như hoàn toàn. Bài này liệt kê các nguyên nhân thật sự hay gặp (gia hạn tự động hỏng, quên nạp lại máy chủ, email nhắc gửi nhầm người) và cách đặt giám sát bằng openssl.
Chuyện gì xảy ra khi chứng chỉ hết hạn
Trình duyệt hiện cảnh báo đỏ toàn màn hình, phần lớn người đọc sẽ rời đi. Ứng dụng di động, API, máy quét mã vạch và các hệ thống khác kết nối tới website cũng báo lỗi. Nếu website đã bật HSTS thì người dùng thậm chí không có nút bỏ qua. Điều đáng nói là sự cố này gần như luôn phòng được.
Thời hạn chứng chỉ cũng đang ngắn dần. Theo Ballot SC-081 của CA/Browser Forum, thời hạn tối đa là 200 ngày từ 15/3/2026 (đã áp dụng), 100 ngày từ 15/3/2027 và 47 ngày từ 15/3/2029, nên chứng chỉ mua theo năm không còn tồn tại và gia hạn thủ công ngày càng khó theo kịp. Let's Encrypt hiện cấp chứng chỉ 90 ngày và theo thông báo của họ sẽ rút xuống 64 ngày từ 10/2/2027 rồi 45 ngày từ 16/2/2028 (lịch có thể thay đổi, hãy xem trang chính thức). Tự động hoá gia hạn là bắt buộc.
Những nguyên nhân hay gặp
- Gia hạn thủ công và quên. Chứng chỉ mua từ nhà cung cấp, người đặt mua đã nghỉ việc, email nhắc gửi vào hộp thư không ai đọc.
- Gia hạn tự động có nhưng đang hỏng âm thầm. Certbot cần chứng minh bạn kiểm soát tên miền. Nó hỏng khi cổng 80 bị tường lửa chặn, bản ghi DNS trỏ sang máy khác, cấu hình web đổi chỗ thư mục, hoặc plugin DNS dùng khoá API đã hết hạn.
- Đã gia hạn nhưng chưa nạp lại. Tệp chứng chỉ mới nằm trên đĩa, nhưng Nginx hay Apache vẫn giữ bản cũ trong bộ nhớ cho đến khi được nạp lại. Phổ biến nhất sau khi dùng
certbot renewmà thiếu bước reload. - Nhiều máy chủ. Chứng chỉ mới cài ở máy A nhưng máy B, bộ cân bằng tải hoặc máy dự phòng còn dùng bản cũ.
- Quên các điểm cuối khác. Website chính ổn nhưng
mail.example.vn,api.example.vn, cổng VPN hay máy chủ nhận thư (cho MTA-STS) dùng chứng chỉ riêng chưa ai theo dõi. - Thông báo không còn. Theo thông báo của Let's Encrypt, họ đã ngừng gửi email nhắc hết hạn từ giữa năm 2025, nên không thể dựa vào email đó làm lớp bảo hiểm.
Một lỗi liên quan nhưng khác bản chất: chứng chỉ sai tên (thiếu www), tự ký, hoặc thiếu chuỗi chứng chỉ trung gian. Trên Nginx hãy dùng fullchain.pem, không phải cert.pem, cho ssl_certificate.
Cách phòng, theo thứ tự ưu tiên
Bật gia hạn tự động và thử nó. Với certbot, chạy thử:
certbot renew --dry-run
Bảo đảm có bộ hẹn giờ chạy (systemctl list-timers | grep certbot) và thêm bước nạp lại tự động sau khi gia hạn. Cách bền nhất là tạo một tệp thực thi trong thư mục hook của certbot, vì certbot luôn chạy mọi hook ở đó sau mỗi lần gia hạn thành công:
#!/bin/sh
# /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh (chmod +x)
systemctl reload nginx
(thay nginx bằng apache2 nếu cần). Đừng chỉ dựa vào certbot renew --deploy-hook: tuỳ chọn đó chỉ được lưu lại khi có chứng chỉ thật sự được gia hạn trong lần chạy đó. Đặt giám sát độc lập, vì cơ chế gia hạn có thể hỏng mà không ai biết. Một tập lệnh nhỏ chạy hằng ngày:
#!/bin/sh
host="example.vn"
if ! echo | openssl s_client -connect "$host:443" -servername "$host" 2>/dev/null \
| openssl x509 -noout -checkend 1814400 >/dev/null 2>&1; then
echo "Chung chi cua $host het han trong 21 ngay hoac khong doc duoc"
fi
Lệnh -checkend 1814400 kiểm tra chứng chỉ có hết hạn trong 21 ngày (1.814.400 giây) tới không; nếu có thì lệnh trả mã lỗi và tập lệnh in cảnh báo. Dòng >/dev/null 2>&1 ngăn openssl in "Certificate will not expire" mỗi lần chạy (nếu không, cron sẽ gửi email hằng ngày). Với chứng chỉ chỉ sống 45 đến 64 ngày, ngưỡng cảnh báo 21 ngày vẫn hợp lý. Gắn đầu ra vào email gửi đến một địa chỉ nhóm (không phải email cá nhân) hoặc một công cụ giám sát uptime. Cuối cùng, thêm một nhắc lịch trước hạn 30 ngày gắn với ít nhất hai người.
Theo nền tảng
Cloudflare. Chứng chỉ Universal SSL ở mặt trước (giữa Cloudflare và người đọc) do Cloudflare tự gia hạn. Nhưng chứng chỉ ở máy chủ gốc vẫn là việc của bạn; chế độ SSL/TLS nên là Full (strict) kèm chứng chỉ hợp lệ ở máy chủ gốc, ví dụ Let's Encrypt hoặc chứng chỉ Origin của Cloudflare (thời hạn dài, chỉ tin cậy với Cloudflare).
cPanel. Vào SSL/TLS Status, chọn tên miền và bấm Run AutoSSL. Kiểm tra AutoSSL đang bật (WHM hoặc nhờ nhà cung cấp hosting). AutoSSL không cấp được khi tên miền không còn trỏ về máy chủ này (ví dụ đã chuyển qua proxy hoặc dịch vụ khác).
DirectAdmin. Vào SSL Certificates, chọn Free & automatic certificate from Let's Encrypt, tích tên miền và www, bấm Save. Hệ thống tự gia hạn; vẫn nên xem lại ngày hết hạn sau khi thay đổi cấu hình.
Nginx. Sau khi gia hạn, kiểm tra cấu hình bằng nginx -t rồi nạp lại bằng systemctl reload nginx. Dùng ssl_certificate trỏ vào fullchain.pem.
Apache. Sau khi gia hạn, nạp lại bằng systemctl reload apache2 (hoặc httpd). Apache 2.4.8 trở lên dùng fullchain cho SSLCertificateFile; bản cũ hơn cần thêm SSLCertificateChainFile.
Cách tự kiểm tra
Xem ngày hết hạn, tên và nhà phát hành của chứng chỉ mà máy chủ đang thực sự phục vụ:
echo | openssl s_client -connect example.vn:443 -servername example.vn 2>/dev/null | openssl x509 -noout -dates -subject -issuer
Dòng notAfter là ngày hết hạn. Kiểm tra cả chuỗi chứng chỉ:
echo | openssl s_client -connect example.vn:443 -servername example.vn -verify_hostname example.vn 2>/dev/null | grep "Verify return code"
Kết quả mong muốn là Verify return code: 0 (ok). Tuỳ chọn -verify_hostname là bắt buộc để kiểm tra tên: thiếu nó, OpenSSL chỉ kiểm tra chuỗi chứng chỉ và vẫn báo 0 (ok) dù chứng chỉ không thuộc tên miền đó (-servername chỉ gửi tên cho máy chủ chọn chứng chỉ); khi tên sai bạn sẽ thấy 62 (hostname mismatch). Lặp lại cho mọi tên miền con đang dùng HTTPS. Hệ thống không nằm ở cổng 443 (máy chủ nhận thư, VPN) thì đổi cổng trong lệnh.
Nếu website đã bật HSTS, hãy đọc thêm bài HSTS: bật thế nào mà không tự khoá website để hiểu vì sao chứng chỉ hết hạn lúc này nghiêm trọng hơn.