2 giờ sáng. Điện thoại rung. Alert từ monitoring: “SSL certificate expired — site returning ERR_CERT_DATE_INVALID”. Khách hàng gọi báo trình duyệt hiện cảnh báo đỏ chói, doanh thu đang chảy máu từng phút.
Tình huống này mình gặp lần đầu cách đây 2 năm trên một server production Ubuntu 20.04. Lúc đó chứng chỉ Let’s Encrypt cài tay bằng apt, quên setup auto-renew, và đúng lúc hết hạn thì không ai để ý. Từ hôm đó mình thề không để chuyện đó xảy ra lần nữa.
Vấn đề thực tế — Tại sao site đang hiện “Not Secure”?
Ba tình huống dưới đây chiếm khoảng 95% cases mình từng debug:
- Chưa cài SSL từ đầu: Server mới setup, chạy HTTP thuần, Chrome hiện “Not Secure” trên thanh địa chỉ, Google Search Console cảnh báo.
- Chứng chỉ hết hạn: Let’s Encrypt có hạn 90 ngày. Nếu auto-renew không chạy được — firewall block port 80, DNS thay đổi, cronjob chết — chứng chỉ tự động hết hạn không báo trước.
- Cài rồi nhưng web server không load: Certbot chạy thành công nhưng Nginx/Apache config chưa reload đúng cách, site vẫn serve HTTP.
Mỗi tình huống có nguyên nhân và cách fix khác nhau — mình sẽ cover từng cái bên dưới.
Tại sao Let’s Encrypt hay lỗi?
Let’s Encrypt không cấp cert cho bất kỳ ai xin. Cơ chế ACME hoạt động kiểu này: bạn bảo “tôi sở hữu example.com”, server của Let’s Encrypt bảo “vậy thì chứng minh đi” — bằng cách đặt một file đặc biệt lên web server hoặc thêm TXT record vào DNS. Verify xong mới cấp chứng chỉ 90 ngày.
Hoàn toàn tự động — khi mọi thứ hoạt động. Vấn đề là nếu bất kỳ thứ gì trong chain bị block thì challenge thất bại, không có cert.
Nguyên nhân hay gặp nhất:
- Port 80 bị block bởi UFW hoặc security group trên cloud (AWS/GCP/DigitalOcean)
- DNS A record chưa trỏ đúng về IP server — hay gặp khi migrate server, DNS cần 15–30 phút để propagate
- Certbot phiên bản cũ từ
aptcó nhiều bug đã được fix ở bản snap - Web server đang giữ port 80 trong khi certbot dùng standalone mode
Ba cách cài certbot — nên chọn cái nào?
Bước 0: Kiểm tra trước khi làm gì
Đừng cài gì vội. Check mấy thứ cơ bản này trước:
# Kiểm tra DNS đã trỏ về server chưa
dig +short yourdomain.com
# Kiểm tra port 80 có mở không
sudo ufw status
curl -I http://yourdomain.com
# Kiểm tra certbot cũ đã cài chưa
certbot --version
Nếu dig trả về đúng IP của server và curl connect được — sẵn sàng rồi, bắt đầu thôi.
Cách 1: Certbot qua Snap (khuyến nghị)
Snap là cách Let’s Encrypt chính thức khuyến nghị từ 2021. Lý do đơn giản: luôn là phiên bản mới nhất, tự update, không bị conflict với hệ thống.
Một lưu ý trước khi bắt đầu: Let’s Encrypt giới hạn 5 chứng chỉ duplicate per domain per tuần. Nếu bạn test nhiều lần fail liên tục, thêm flag --staging để tránh cạn quota thật.
# Gỡ certbot cũ nếu đã cài bằng apt (tránh conflict)
sudo apt remove certbot
# Cài snap certbot
sudo snap install --classic certbot
# Tạo symlink để dùng lệnh certbot trực tiếp
sudo ln -s /snap/bin/certbot /usr/bin/certbot
Lấy chứng chỉ — certbot có plugin riêng cho Nginx và Apache, tự động sửa config luôn:
# Nếu dùng Nginx
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com
# Nếu dùng Apache
sudo certbot --apache -d yourdomain.com -d www.yourdomain.com
# Nếu chưa setup web server (standalone mode)
sudo certbot certonly --standalone -d yourdomain.com
Trong quá trình chạy, certbot hỏi email để nhận cảnh báo expiry và yêu cầu đồng ý ToS. Xong xuôi, nó tự sửa nginx config: thêm SSL block, cấu hình cipher suites, và redirect HTTP → HTTPS.
Cách 2: Cài bằng apt (Ubuntu 20.04 trở xuống)
Không muốn dùng snap thì vẫn có apt:
sudo apt update
sudo apt install certbot python3-certbot-nginx
sudo certbot --nginx -d yourdomain.com
Nhược điểm thực tế: phiên bản trong apt repo của Ubuntu thường chậm hơn snap vài tháng. Mình từng gặp bug với certbot 1.12 từ apt mà bản 2.x snap đã fix từ lâu.
Cách 3: Wildcard certificate với DNS challenge
Có nhiều subdomain (api.example.com, app.example.com, cdn.example.com…) và muốn dùng 1 chứng chỉ cho tất cả? Wildcard là giải pháp:
sudo certbot certonly \
--manual \
--preferred-challenges dns \
-d "*.yourdomain.com" \
-d yourdomain.com
DNS challenge yêu cầu thêm TXT record vào DNS thủ công — không tự động được nếu không có DNS API. Mình chỉ dùng cách này khi có Cloudflare API key để automation, còn không thì cài từng subdomain riêng cho đỡ phức tạp.
Setup production-ready — quy trình mình dùng từ 2024
Sau lần cert hết hạn lúc 2 giờ sáng đó, mình đã test kỹ quy trình này trên staging Ubuntu 22.04 trước khi áp lên production. Đây là toàn bộ những gì cần làm:
1. Cài và lấy chứng chỉ với Nginx
# Đảm bảo Nginx đang chạy
sudo systemctl status nginx
# Cài certbot snap
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
# Lấy chứng chỉ — certbot tự sửa nginx config
sudo certbot --nginx -d yourdomain.com -d www.yourdomain.com \
--email [email protected] \
--agree-tos \
--no-eff-email
Flag --no-eff-email để không đăng ký nhận email quảng cáo từ EFF.
2. Verify auto-renewal
Certbot snap tự cài systemd timer để tự renew. Kiểm tra ngay sau khi cài:
# Kiểm tra timer đang active
sudo systemctl status snap.certbot.renew.timer
# Test thử quá trình renew (không thực sự renew)
sudo certbot renew --dry-run
--dry-run mô phỏng toàn bộ quá trình renew mà không thay đổi cert thật. Nếu lỗi ở đây thì sẽ lỗi thật khi cert tự động renew — tốt hơn là phát hiện sớm lúc này.
3. Kiểm tra chứng chỉ đã cài
# Xem tất cả chứng chỉ và ngày hết hạn
sudo certbot certificates
# Output mẫu:
# Found the following certs:
# Certificate Name: yourdomain.com
# Domains: yourdomain.com www.yourdomain.com
# Expiry Date: 2026-05-28 (VALID: 89 days)
# Certificate Path: /etc/letsencrypt/live/yourdomain.com/fullchain.pem
4. Setup deploy hook để reload Nginx sau khi renew
Sau khi certbot renew, cần reload web server mới áp dụng chứng chỉ mới. Certbot snap thường tự handle việc này — nhưng không phải lúc nào cũng đáng tin. Tốt nhất tạo deploy hook tường minh cho chắc:
# Kiểm tra deploy hooks hiện có
ls /etc/letsencrypt/renewal-hooks/deploy/
# Tạo hook tự reload nginx sau khi renew thành công
sudo tee /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh << 'EOF'
#!/bin/bash
nginx -t && systemctl reload nginx
EOF
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-nginx.sh
Từ giờ mỗi khi certbot renew thành công, nginx tự reload. Không còn cảnh chứng chỉ mới đã cài nhưng nginx vẫn serve cert cũ hết hạn.
Xử lý các lỗi hay gặp
Lỗi: “Problem binding to port 80”
# Nginx đang block port 80 — dừng tạm, lấy cert, rồi restart
sudo systemctl stop nginx
sudo certbot certonly --standalone -d yourdomain.com
sudo systemctl start nginx
Lỗi: “Connection timed out”
# Port 80 bị UFW block
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw reload
Nếu dùng cloud, UFW chỉ là một lớp tường lửa — còn phải mở port trong Security Group (AWS) hoặc Firewall rules (GCP/DigitalOcean) nữa.
Renew khẩn cấp khi chứng chỉ đã hết hạn
# Force renew ngay lập tức
sudo certbot renew --force-renewal
# Reload nginx sau khi renew
sudo nginx -t && sudo systemctl reload nginx
Kiểm tra SSL grade sau khi cài
# Check nhanh từ terminal
curl -vI https://yourdomain.com 2>&1 | grep -E "SSL|TLS|expire|issuer"
Muốn check kỹ hơn thì dùng SSL Labs tại ssllabs.com/ssltest. Nó chấm điểm từ A+ đến F, chỉ ra đúng chỗ config yếu — cipher suite lỗi thời, HSTS chưa bật, v.v. Site cấu hình đúng với certbot mặc định thường đạt A hoặc A+.
Cuối cùng, đừng bỏ qua email của Let’s Encrypt. Họ cảnh báo ở mốc 30 ngày, 20 ngày và 10 ngày trước khi hết hạn — nếu bạn đăng ký email lúc chạy certbot. Nhận được mail mà auto-renew vẫn không chạy được nghĩa là có gì đó cần debug ngay. Đừng chờ đến ngày hết hạn thật sự. Lúc đó có thể là 2 giờ sáng.

