Sổ tay kỹ thuật

Let's Encrypt với certbot – http-01, dns-01, auto renew

how-to#tls#letsencrypt#nginx· cập nhật 2026-11-06

Dùng khi cần cert TLS miễn phí cho Nginx, cert wildcard qua DNS, và đảm bảo cert tự gia hạn không ai phải nhớ.

Sửa trang này
Mục lục

Tóm tắt: Cài certbot (snap hoặc gói distro), chọn challenge: http-01 (cổng 80 mở ra internet, plugin --nginx hoặc --webroot) hoặc dns-01 (bắt buộc cho wildcard, dùng plugin DNS như dns-cloudflare). Certbot tạo timer systemd tự renew; thêm --deploy-hook để reload Nginx. Luôn thử với --dry-run / --test-cert trước.

Luồng hoạt động

flowchart LR A[certbot] -->|"tạo order"| B[Let's Encrypt ACME] B -->|"challenge token"| A A -->|"http-01: ghi file"| C["/.well-known/acme-challenge/"] A -->|"dns-01: tạo TXT"| D["_acme-challenge.example.com"] B -->|"kiểm tra"| C B -->|"kiểm tra"| D B -->|"cấp cert"| E["/etc/letsencrypt/live/example.com/"] E --> F["nginx reload (deploy-hook)"]
Challenge Yêu cầu Wildcard Khi nào dùng
http-01 Cổng 80 của domain trỏ về server, truy cập được từ internet Không Web server public thông thường
dns-01 API token sửa DNS zone Có Wildcard, server nội bộ không mở cổng 80, nhiều server dùng chung cert
tls-alpn-01 Cổng 443 Không Ít dùng với certbot

Cấu hình

Cài đặt

# Cách được certbot khuyên dùng: snap
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot
 
# Hoặc gói distro (Ubuntu/Debian), thường cũ hơn
sudo apt install certbot python3-certbot-nginx

http-01 với Nginx

# Plugin nginx: tự sửa server block, thêm redirect HTTPS
sudo certbot --nginx -d example.com -d www.example.com \
  -m ops@example.com --agree-tos --non-interactive
 
# Chỉ lấy cert, tự cấu hình Nginx (an toàn hơn khi config Nginx được quản lý bằng Ansible/Git)
sudo certbot certonly --webroot -w /var/www/letsencrypt \
  -d example.com -d www.example.com --deploy-hook "systemctl reload nginx"

Với --webroot, Nginx phải phục vụ thư mục challenge qua HTTP:

/etc/nginx/conf.d/example.com.conf
server {
    listen 80;
    server_name example.com www.example.com;
 
    location /.well-known/acme-challenge/ {
        root /var/www/letsencrypt;
    }
    location / { return 301 https://$host$request_uri; }
}
 
server {
    listen 443 ssl;
    http2 on;
    server_name example.com www.example.com;
 
    ssl_certificate     /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols       TLSv1.2 TLSv1.3;
}

Luôn dùng fullchain.pem (leaf + intermediate), không dùng cert.pem. Dùng cert.pem là nguyên nhân phổ biến của lỗi thiếu chain, xem TLS troubleshooting.

http2 on; cần Nginx ≥ 1.25.1; bản cũ hơn viết listen 443 ssl http2;. Cấu hình reverse proxy đầy đủ xem Nginx reverse proxy + TLS.

dns-01 và wildcard (Cloudflare)

# Cài plugin (bản snap)
sudo snap set certbot trust-plugin-with-root=ok
sudo snap install certbot-dns-cloudflare
/root/.secrets/cloudflare.ini
# API token chỉ có quyền Zone:DNS:Edit cho zone cần thiết
dns_cloudflare_api_token = <CLOUDFLARE_API_TOKEN>
sudo chmod 600 /root/.secrets/cloudflare.ini
sudo certbot certonly --dns-cloudflare \
  --dns-cloudflare-credentials /root/.secrets/cloudflare.ini \
  -d example.com -d '*.example.com' \
  --deploy-hook "systemctl reload nginx"

DNS provider không có plugin: --manual --preferred-challenges dns cho phép tạo TXT bằng tay, nhưng không tự renew được (trừ khi viết --manual-auth-hook).

Auto renew

Mục đích Lệnh
Liệt kê cert, ngày hết hạn sudo certbot certificates
Thử renew không cấp thật sudo certbot renew --dry-run
Xem timer (snap) systemctl list-timers | grep certbot
Log renew sudo less /var/log/letsencrypt/letsencrypt.log
Ép renew một cert sudo certbot renew --cert-name example.com --force-renewal
Thêm domain vào cert sẵn có sudo certbot certonly --cert-name example.com -d example.com -d api.example.com ...
Xoá cert sudo certbot delete --cert-name example.com
Thử trên môi trường staging (không tính rate limit) thêm --test-cert

Hook chạy sau mỗi lần renew thành công có thể đặt cố định thay vì truyền tham số:

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

Cấu hình renew của từng cert nằm ở /etc/letsencrypt/renewal/<cert-name>.conf. Certbot renew khi cert còn dưới 30 ngày.

Lỗi thường gặp

Triệu chứng Nguyên nhân Cách xử lý
Timeout during connect (likely firewall problem) Cổng 80 bị chặn (firewall, security group, NAT) Mở 80 từ internet, hoặc chuyển sang dns-01
Invalid response from .../acme-challenge/...: 404 Webroot sai, hoặc location / redirect/proxy nuốt request challenge Kiểm tra -w khớp root trong Nginx; đặt location /.well-known/acme-challenge/ riêng
DNS problem: NXDOMAIN / sai IP Bản ghi A/AAAA chưa trỏ đúng; có AAAA cũ trỏ IPv6 không phục vụ dig +short A example.com, dig +short AAAA example.com
dns-01 fail Incorrect TXT record DNS chưa propagate hoặc token thiếu quyền Tăng --dns-cloudflare-propagation-seconds; kiểm tra quyền token
too many certificates already issued Chạm rate limit khi test lặp lại Dùng --test-cert/--dry-run khi thử; chờ hết cửa sổ rate limit
Renew thành công nhưng site vẫn dùng cert cũ Nginx chưa reload Thêm deploy hook, systemctl reload nginx
Cert hết hạn dù có timer Timer bị disable, hook lỗi, hoặc cert --manual systemctl list-timers, certbot renew --dry-run, đọc log

Tham khảo