HTTPS giờ là mặc định. Trình duyệt đánh dấu trang HTTP là "Không an toàn", và nhiều tính năng web chỉ chạy trên HTTPS. Let's Encrypt cấp chứng chỉ SSL/TLS miễn phí, còn Certbot là công cụ tự xin, cài và gia hạn chứng chỉ đó. Bài này giúp bạn bật HTTPS cho website chạy Nginx trên Ubuntu, tự động chuyển HTTP sang HTTPS và chắc chắn chứng chỉ tự gia hạn đúng hạn.

Bạn cần chuẩn bị:

  • VPS Ubuntu 22.04 hoặc 24.04 đã cài Nginx, có server block với server_name example.com www.example.com;.
  • Bản ghi A của example.com và www.example.com trỏ về IP VPS (ví dụ 203.0.113.10), đã lan truyền xong.
  • Cổng 80 và 443 mở trên tường lửa. Let's Encrypt cần truy cập cổng 80 để xác thực.

Bước 1: Kiểm tra DNS và cổng 80

Certbot thất bại phần lớn vì DNS chưa trỏ đúng. Kiểm tra trước:

dig +short example.com A
dig +short www.example.com A
sudo ufw status | grep -E '80|443|Nginx'

Cả hai tên phải trả về 203.0.113.10. Nếu có bản ghi AAAA (IPv6), nó cũng phải trỏ đúng VPS, vì Let's Encrypt ưu tiên IPv6 khi có. Bản ghi AAAA sai là nguyên nhân lỗi khó phát hiện.

dig +short example.com AAAA

Bước 2: Cài Certbot qua snap

Tài liệu chính thức của Certbot khuyến nghị cài qua snap để luôn có bản mới. Gỡ bản cài từ apt nếu có, tránh xung đột:

sudo apt remove -y certbot
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
certbot --version

Snap đã có sẵn trên Ubuntu Server. Nếu lệnh ln báo file đã tồn tại, bạn có thể bỏ qua.

Bước 3: Xin và cài chứng chỉ cho Nginx

Plugin --nginx tự đọc server block, xác thực tên miền và sửa cấu hình để bật HTTPS.

sudo certbot --nginx -d example.com -d www.example.com

Lần đầu, Certbot hỏi email và yêu cầu đồng ý điều khoản. Email dùng cho thông báo quan trọng về tài khoản. Khi xong, bạn sẽ thấy đường dẫn chứng chỉ:

/etc/letsencrypt/live/example.com/fullchain.pem
/etc/letsencrypt/live/example.com/privkey.pem

Certbot bản mới mặc định thêm chuyển hướng HTTP sang HTTPS. Mở lại file cấu hình để xem các dòng được thêm, có ghi chú # managed by Certbot:

sudo cat /etc/nginx/sites-available/example.com
sudo nginx -t && sudo systemctl reload nginx

Nếu chỉ muốn lấy chứng chỉ mà không cho Certbot sửa cấu hình Nginx, dùng sudo certbot certonly --nginx -d example.com -d www.example.com, sau đó tự khai báo ssl_certificate và ssl_certificate_key.

Bước 4: Kiểm tra cơ chế tự gia hạn

Bản snap cài sẵn một systemd timer chạy hai lần mỗi ngày. Certbot chỉ gia hạn khi chứng chỉ gần hết hạn, nên chạy thường xuyên không gây hại.

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

Lệnh --dry-run thử gia hạn với máy chủ staging, không cấp chứng chỉ thật. Nếu kết quả báo Congratulations, all simulated renewals succeeded, cơ chế tự gia hạn đã ổn. Xem danh sách chứng chỉ và ngày hết hạn:

sudo certbot certificates

Bước 5: Thêm hook reload dịch vụ sau khi gia hạn

Với plugin --nginx, Certbot tự reload Nginx. Nhưng nếu chứng chỉ còn dùng cho dịch vụ khác (mail server, ứng dụng Docker…), bạn cần hook. Script trong thư mục renewal-hooks/deploy chạy sau mỗi lần gia hạn thành công:

sudo nano /etc/letsencrypt/renewal-hooks/deploy/reload-services.sh
#!/bin/sh
systemctl reload nginx
sudo chmod +x /etc/letsencrypt/renewal-hooks/deploy/reload-services.sh

Bước 6: Cấp chứng chỉ wildcard bằng xác thực DNS (tùy chọn)

Chứng chỉ *.example.com chỉ cấp được qua DNS-01. Cách thủ công:

sudo certbot certonly --manual --preferred-challenges dns -d example.com -d '*.example.com'

Certbot sẽ yêu cầu tạo bản ghi TXT _acme-challenge.example.com với một chuỗi ngẫu nhiên. Đợi bản ghi lan truyền rồi mới nhấn Enter. Kiểm tra bằng:

dig +short TXT _acme-challenge.example.com @1.1.1.1

Lưu ý: chứng chỉ cấp bằng --manual không tự gia hạn được. Để tự động, hãy dùng plugin DNS phù hợp với nơi quản lý DNS của bạn, ví dụ certbot-dns-cloudflare, kèm API token.

Bước 7: Theo dõi hạn chứng chỉ

Let's Encrypt đã ngừng gửi email nhắc hết hạn từ năm 2025. Theo kế hoạch đã công bố, thời hạn chứng chỉ mặc định sẽ giảm từ 90 ngày xuống 64 ngày (tháng 2/2027) rồi 45 ngày (tháng 2/2028). Vì vậy bạn nên:

  • Không tự viết cron gia hạn theo số ngày cố định. Hãy để Certbot quyết định thời điểm.
  • Dùng một dịch vụ giám sát uptime có kiểm tra hạn SSL để nhận cảnh báo.
  • Tự kiểm tra định kỳ bằng lệnh dưới đây.
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates -issuer

Kiểm tra lại

  • Truy cập http://example.com: phải tự chuyển sang https://example.com với mã 301.
  • curl -I http://example.com trả về 301 và header Location: https://example.com/.
  • sudo certbot renew --dry-run thành công.
  • Trong WordPress, vào Settings, General, đổi cả hai địa chỉ sang https://.
  • Mở DevTools, tab Console, xem có cảnh báo "Mixed Content" không.

Lỗi thường gặp và cách xử lý

Timeout during connect (likely firewall problem)

Let's Encrypt không kết nối được cổng 80. Kiểm tra UFW, firewall của nhà cung cấp (security group) và chắc chắn Nginx đang lắng nghe: sudo ss -tlnp | grep ':80'.

Invalid response from http://example.com/.well-known/acme-challenge/... 404

Yêu cầu xác thực tới nhầm máy chủ hoặc nhầm server block. Nguyên nhân hay gặp: bản ghi AAAA trỏ sai, DNS chưa lan truyền, hoặc có quy tắc chặn file ẩn mà không loại trừ .well-known.

Could not automatically find a matching server block

Certbot không tìm thấy server_name khớp với tên miền. Thêm đúng server_name example.com www.example.com; vào server block, chạy sudo nginx -t, reload rồi chạy lại Certbot.

Too many certificates already issued

Bạn đã chạm giới hạn tần suất cấp chứng chỉ, thường do thử đi thử lại. Khi thử nghiệm, hãy thêm --dry-run hoặc --test-cert để dùng máy chủ staging, không tính vào giới hạn.

Mixed Content sau khi bật HTTPS

Nội dung cũ còn liên kết http://. Với WordPress, dùng WP-CLI: wp search-replace 'http://example.com' 'https://example.com' --all-tables --dry-run, kiểm tra kết quả rồi chạy lại không có --dry-run. Nhớ sao lưu database trước.