cách cài Certbot cho Apache trên Ubuntu 24.04
Hướng dẫn cài Certbot qua apt trên Ubuntu 24.04 để lấy chứng chỉ Let's Encrypt. Lưu ý lỗi thiếu ServerName trong vhost khiến quá trình cấp cert thất bại.
Những gì bạn đang xây dựng
Một site Apache trên Ubuntu 24.04 chạy HTTPS với chứng chỉ Let's Encrypt miễn phí, được trình duyệt tin cậy — được cấp bởi Certbot, tự động gia hạn bằng một systemd timer mà bạn sẽ không bao giờ phải bận tâm đến nữa. Câu lệnh thực hiện công việc này chỉ vỏn vẹn một dòng. Mọi lỗi phát sinh đều xảy ra trước dòng lệnh đó: một vhost thiếu ServerName, port 80 bị đóng tại firewall của nhà cung cấp, hoặc DNS vẫn đang trỏ về server cũ. Vì vậy, hướng dẫn này dành phần lớn thời gian để nói về các điều kiện tiên quyết, và nêu chính xác chuỗi lỗi mà mỗi sai sót sẽ in ra.
Hai lưu ý về phạm vi áp dụng. Nếu web server của bạn là nginx, quy trình tương tự nhưng plugin và các file config sẽ khác — hãy sử dụng phiên bản nginx của hướng dẫn này thay thế. Và nếu thứ bạn đang bảo mật chỉ dùng nội bộ — như một admin panel trên địa chỉ private, hoặc một staging box không ai truy cập — bạn không cần CA (Certificate Authority) chút nào; một self-signed certificate sẽ ít phức tạp hơn và hoạt động được khi offline.
Điều kiện tiên quyết, và ba lý do khiến mọi thứ thất bại trước khi Certbot kịp chạy
- Apache đã chạy site của bạn qua HTTP thường. Apache plugin của Certbot sẽ chỉnh sửa một site đã tồn tại; nó không tạo mới. Nếu bạn bắt đầu từ một VPS trống, hãy cài đặt LAMP stack trên Ubuntu 24.04 trước rồi quay lại đây — hướng dẫn này là chương về TLS còn thiếu của nó.
- Một domain công khai có bản ghi A trỏ về địa chỉ VPS của bạn. Let's Encrypt HTTP-01 challenge có nghĩa là các server xác thực của họ sẽ kết nối tới máy của bạn từ internet: không dùng NAT homelab nếu không có port forward, không dùng
.localnames, không dùng IP thuần.dig +short example.comphải trả về địa chỉ VPS của bạn, và nếu bạn vừa đổi DNS trong vòng một giờ qua, hãy đợi TTL của bản ghi cũ hết hạn trước khi cấp phát. - Nếu có bản ghi AAAA, nó phải chính xác. Let's Encrypt ưu tiên IPv6 khi có bản ghi AAAA được công bố, vì vậy một bản ghi AAAA cũ/sai sẽ làm thất bại quá trình xác thực ngay cả khi
curltừ laptop của bạn — thường là qua IPv4 — vẫn hoạt động bình thường. Hãy công bố một bản ghi AAAA chính xác hoặc đừng công bố gì cả.
Port 80 và 443 cần phải được mở trong ufw và trong firewall mạng của nhà cung cấp — hầu hết các bảng điều khiển hosting đều có một firewall thứ hai mà OS không nhìn thấy. HTTP-01 xác thực cụ thể qua port 80; bạn không thể chỉ chạy mỗi port 443.
sudo ufw allow "Apache Full"
sudo ufw statusKhi đã chuẩn bị xong các bước trên, toàn bộ công việc chỉ mất 15 phút, mà 10 phút trong đó là để đọc.
Dùng Snap hay apt cho Certbot? Trên 24.04, apt cuối cùng đã ổn
Certbot đã chuyển sang phân phối qua snap từ nhiều năm trước vì một lý do chính đáng: các package của distro thường bị lỗi thời. Ubuntu 20.04 đi kèm Certbot 0.40 và không bao giờ cập nhật, và dự án đã mệt mỏi vì phải debug các lỗi từ 5 năm trước. Trên 24.04 lý do đó không còn nữa — kho lưu trữ cung cấp Certbot 2.9.0, một bản release thế hệ mới, và unattended-upgrades giúp nó luôn được patch. Khuyến nghị của tôi cho OS này: hãy dùng apt. Bạn sẽ bỏ qua được daemon snapd, Apache plugin được cài đặt trong cùng một transaction, và timer gia hạn sẽ tích hợp với systemd theo cách Debian thông thường.
sudo apt update
sudo apt install -y certbot python3-certbot-apache
certbot --versionKết quả đúng: certbot 2.9.0. Package python3-certbot-apache là plugin dùng để đọc và chỉnh sửa các file config Apache của bạn — nếu không có nó, certbot --apache sẽ lỗi với The requested apache plugin does not appear to be installed.
Dùng snap vẫn là lựa chọn đúng trong hai trường hợp: bạn muốn có Certbot mới nhất ngay khi nó vừa release, hoặc bạn cần một DNS plugin mà chỉ được phân phối dưới dạng snap (một vài plugin của certbot-dns-* là như vậy). Nếu bạn chọn cách đó:
sudo apt remove -y certbot python3-certbot-apache
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbotDù bạn chọn cách nào, đừng bao giờ chạy cả hai. Hai bản cài đặt sẽ dẫn đến hai bộ lập lịch gia hạn tranh giành /etc/letsencrypt, và certbot mà shell của bạn tìm thấy trong PATH có thể không phải là cái đang quản lý các certificate của bạn. Dòng apt remove ở trên không phải là phần trang trí tùy chọn.
Vhost mà Certbot chỉnh sửa phải tồn tại sẵn — ServerName là yếu tố then chốt
certbot --apache hoạt động bằng cách tìm virtual host port-80 có ServerName hoặc ServerAlias khớp với mỗi domain -d mà bạn truyền vào, chứng minh quyền kiểm soát domain thông qua đó, sau đó viết một bản sao SSL của vhost đó. Không có ServerName khớp, sẽ không có kết quả — và vhost mặc định của Ubuntu 000-default.conf đi kèm với ServerName đã bị comment. Dòng bị comment đó chính là lý do phổ biến nhất khiến lệnh lớn duy nhất trong hướng dẫn này thất bại.
Vì vậy, trước khi chạm vào Certbot, hãy cấp cho site một vhost dựa trên tên (name-based) chuẩn chỉnh. Tạo /etc/apache2/sites-available/example.com.conf:
<VirtualHost *:80>
ServerName example.com
ServerAlias www.example.com
DocumentRoot /var/www/example.com
ErrorLog ${APACHE_LOG_DIR}/example.com-error.log
CustomLog ${APACHE_LOG_DIR}/example.com-access.log combined
</VirtualHost>Enable nó và xác nhận Apache vừa parse được vừa route tên đến nó:
sudo a2ensite example.com.conf
sudo apache2ctl configtest
sudo systemctl reload apache2
sudo apache2ctl -Sconfigtest phải in ra Syntax OK. Nếu nó in thêm cả AH00558: apache2: Could not reliably determine the server's fully qualified domain name, đó là cảnh báo về ServerName toàn cục (global), không phải của vhost bạn — không sao cả, và nó sẽ được làm im lặng bởi echo "ServerName $(hostname -f)" | sudo tee /etc/apache2/conf-available/servername.conf && sudo a2enconf servername && sudo systemctl reload apache2.
Output của -S mới là phần kiểm tra quan trọng. Bạn cần một dòng như port 80 namevhost example.com (/etc/apache2/sites-enabled/example.com.conf:1) với alias www.example.com ở bên dưới — Apache báo cáo symlink sites-enabled mà nó thực sự đọc, chứ không phải file bạn đã edit trong sites-available. Nếu example.com không được liệt kê cho port 80, Certbot cũng sẽ không tìm thấy nó.
Cấp chứng chỉ: certbot --apache
sudo certbot --apache -d example.com -d www.example.comLần chạy đầu tiên sẽ hỏi ba thứ: một địa chỉ email (dùng cho tài khoản ACME và các thông báo khẩn từ CA; Let's Encrypt không còn gửi cảnh báo hết hạn, nên việc theo dõi gia hạn là việc của bạn), đồng ý với các điều khoản của Let's Encrypt, và việc có chia sẻ email với EFF hay không. Không còn câu hỏi về redirect nữa: từ Certbot 2.0, trình cài đặt Apache mặc định redirect HTTP sang HTTPS, đúng như bạn muốn. Truyền tham số --no-redirect nếu bạn thực sự cần giữ HTTP thuần để tiếp tục phục vụ content.
Thành công sẽ trông như thế này, và bạn nên đọc kỹ thay vì chỉ lướt qua:
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/example.com/fullchain.pem
Key is saved at: /etc/letsencrypt/live/example.com/privkey.pem
This certificate expires on 2026-10-14.
Deploying certificate
Successfully deployed certificate for example.com to /etc/apache2/sites-available/example.com-le-ssl.conf
Successfully deployed certificate for www.example.com to /etc/apache2/sites-available/example.com-le-ssl.conf
Congratulations! You have successfully enabled HTTPS on https://example.com and https://www.example.comĐằng sau thông báo đó, Certbot đã làm bốn việc: enable module ssl của Apache nếu nó chưa được bật, viết file example.com-le-ssl.conf — một bản sao vhost của bạn tại *:443 với SSLEngine on và các đường dẫn certificate — enable nó, và thêm một block RewriteRule vào vhost port-80 gốc để 301 mọi thứ sang HTTPS. File vhost gốc của bạn được chỉnh sửa chứ không bị thay thế, và bản sao SSL nằm ngay cạnh nó để bạn có thể đọc mọi dòng mà nó đã thêm vào.
Certificate thực sự nằm ở đâu, và tại sao bạn không bao giờ được copy nó
Mọi thứ nằm trong /etc/letsencrypt/live/example.com/: fullchain.pem (certificate cùng với intermediate chain — thứ mà các server nên trỏ tới), privkey.pem (private key, chỉ root mới đọc được), cộng với cert.pem và chain.pem cho các phần mềm muốn tách rời các thành phần. Đây là các symlink trỏ vào /etc/letsencrypt/archive/, và sự gián tiếp này chính là cơ chế gia hạn: quá trình gia hạn sẽ viết file mới vào archive/ và trỏ lại các symlink. Hãy trỏ bất kỳ phần mềm nào khác vào các đường dẫn live/, nó sẽ tự động nhận được các bản gia hạn; nếu bạn copy các file ra chỗ khác, bạn đã tự tạo ra một sự cố sẽ xảy ra sau 90 ngày.
File khác bạn cần biết là /etc/letsencrypt/renewal/example.com.conf, nó ghi lại cách certificate này được cấp — authenticator = apache, installer = apache, các domain — để quá trình gia hạn có thể lặp lại mà không cần người can thiệp, bao gồm cả việc reload Apache sau đó.
Gia hạn đã được lập lịch sẵn — hãy kiểm tra, đừng tự xây dựng thêm
Các certificate của Let's Encrypt có thời hạn 90 ngày theo thiết kế, và package apt đã cài đặt sẵn bộ máy: một systemd timer chạy Certbot hai lần mỗi ngày vào các thời điểm ngẫu nhiên, gia hạn bất kỳ certificate nào còn dưới 30 ngày là hết hạn. Đừng thêm cron job chồng lên; một bộ lập lịch thứ hai không giúp ích gì ngoài việc làm rác log và làm tăng nguy cơ bị dính rate-limit.
systemctl list-timers certbot.timer
sudo certbot renew --dry-runLệnh đầu tiên cho thấy timer đang hoạt động, với thời gian NEXT nằm đâu đó trong 24 giờ tới — lịch trình là hai lần mỗi ngày với độ trễ ngẫu nhiên, nên thời gian chính xác là không thể đoán trước (với bản cài snap, timer là snap.certbot.renew.timer). Lệnh dry run thực hiện một buổi diễn tập gia hạn đầy đủ với môi trường staging của Let's Encrypt — challenge thật, không cấp certificate thật, không tốn rate-limit. Kết quả đúng kết thúc bằng:
Congratulations, all simulated renewals succeeded:
/etc/letsencrypt/live/example.com/fullchain.pem (success)Nếu dry run thất bại, việc gia hạn thật trong ~60 ngày tới cũng sẽ thất bại tương tự — hãy sửa ngay bây giờ, khi certificate hiện tại vẫn còn thời hạn dài. Thủ phạm thường gặp là một rule firewall được thêm vào sau khi cấp phát khiến port 80 bị đóng lại.
Kiểm tra bằng curl, và biểu tượng ổ khóa nên hiển thị gì
curl -sI http://example.com | head -n 3
curl -I https://example.com
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -issuer -datesLệnh đầu tiên nên trả về HTTP/1.1 301 Moved Permanently với header Location: https://example.com/ — đó là redirect mà Certbot đã cài đặt. Lệnh thứ hai nên trả về HTTP/1.1 200 OK mà không có lỗi TLS từ curl. Lệnh thứ ba in ra issuer — một dòng O = Let's Encrypt với CN ngắn như R12 hoặc E7 — và notAfter còn khoảng 90 ngày. Trên trình duyệt, bạn sẽ thấy biểu tượng ổ khóa, và click vào đó sẽ thấy cùng một issuer. Nếu curl chạy tốt mà trình duyệt cảnh báo, khả năng cao là bạn đang xem một trang đã cache hoặc sai hostname, chứ không phải lỗi certificate.
Nhiều site: một certificate SAN hay mỗi site một cert?
Cả hai cách đều được; chúng gia hạn theo cùng một cách. Đối với các site không liên quan trên cùng một máy, hãy chạy lệnh issue một lần cho mỗi site — mỗi site sẽ có thư mục riêng dưới live/ và config gia hạn riêng, và lỗi ở một domain không bao giờ chặn việc gia hạn các domain khác. Đó là cách mặc định của tôi.
Đối với một site có nhiều tên, hãy đưa chúng vào một certificate SAN duy nhất — một cert có thể chứa tới 100 tên. Bạn đã làm điều này ở trên với example.com và www.example.com. Để thêm một tên vào certificate hiện có sau này, hãy reissue bằng cách nêu tên cert và toàn bộ danh sách mới:
sudo certbot --apache --cert-name example.com -d example.com -d www.example.com -d blog.example.comCertbot sẽ nhận thấy tập hợp domain đã thay đổi, yêu cầu bạn xác nhận việc mở rộng, và thay thế certificate tại chỗ — cùng một đường dẫn live/, nên không có gì khác cần chạm vào. Lưu ý rằng danh sách này là thay thế, không phải là thêm vào: nếu bỏ qua www khỏi lệnh đó, certificate mới sẽ âm thầm loại bỏ nó.
Wildcard cần DNS-01, và thường thì bạn không cần wildcard
HTTP-01 không thể cấp *.example.com — việc đặt một file trên web server chỉ chứng minh quyền kiểm soát một hostname, không phải toàn bộ namespace. Wildcard yêu cầu DNS-01 challenge: Certbot sẽ thiết lập một bản ghi TXT tại _acme-challenge.example.com, điều này trên thực tế có nghĩa là cần một plugin certbot-dns-* với API credentials cho DNS provider của bạn, hoặc phải tự tay sửa các bản ghi TXT tại mỗi lần gia hạn bằng --manual (rất cực — đừng lên kế hoạch dựa vào cách này). Hướng dẫn đầy đủ, từ cơ chế bản ghi TXT đến plugin tự động gia hạn, nằm trong wildcard certificates với Certbot qua DNS-01. Lời khuyên chân thành: nếu bạn có bốn subdomain đã biết, một certificate SAN liệt kê cả bốn sẽ đơn giản hơn wildcard và không cần để API keys của DNS trên server.
Các lỗi thường gặp, kèm theo chuỗi lỗi bạn sẽ thấy
Certbot từ chối khởi chạy vì config của Apache bị lỗi.
The apache plugin is not working; there may be problems with your existing configuration.
The error was: MisconfigurationError('Error while running apache2ctl configtest.\n\nAction \'configtest\' failed.\nThe Apache error log may have more information.\n\nAH00526: Syntax error on line 12 of /etc/apache2/sites-enabled/example.com.conf')Plugin sẽ chạy configtest trước khi chạm vào bất cứ thứ gì và dừng lại nếu chính Apache gặp lỗi — các lỗi \n là nguyên văn vì Certbot in ra repr của exception. Hãy tự chạy sudo apache2ctl configtest: nó sẽ chỉ ra file và dòng lỗi — thường là lỗi đánh máy khi sửa tay, một SSLCertificateFile trỏ vào đường dẫn không còn tồn tại, hoặc một module được tham chiếu nhưng chưa được enable. Hãy sửa cho đến khi nó in ra Syntax OK, sau đó chạy lại Certbot.
Không có vhost nào khớp với domain.
Unable to find a virtual host listening on port 80 which is currently the only challenge port.Đây là lỗi thiếu ServerName đã nói ở trên, bị bắt lúc đang issue. Certbot đã tìm kiếm mọi vhost port-80 đang enable để tìm ServerName/ServerAlias khớp với -d của bạn và không thấy gì. sudo apache2ctl -S cho biết Apache thực sự đang route cái gì; hãy thêm dòng ServerName vào đúng vhost, reload, và thử lại. Một lỗi tương tự là validation trỏ vào sai vhost — phản hồi challenge trả về Invalid response ... 404 vì một site khác đã bắt được request. Chẩn đoán và công cụ tương tự: apache2ctl -S.
Validation bị timeout.
Certbot failed to authenticate some domains (authenticator: apache).
...
Detail: ...: Timeout during connect (likely firewall problem)Let's Encrypt không thể mở kết nối TCP tới port 80 tại địa chỉ mà DNS của bạn công bố. Theo thứ tự khả năng xảy ra: firewall mạng của nhà cung cấp (riêng biệt với ufw, được cấu hình trong hosting panel), bộ rule ufw chỉ cho phép 443 hoặc chỉ SSH, DNS vẫn đang trỏ về server cũ, hoặc lỗi AAAA cũ — server của họ thử IPv6, nhưng máy của bạn chỉ trả lời qua IPv4. Hãy test từ bên ngoài VPS: dùng curl -I http://example.com từ laptop của bạn để tái hiện lại những gì validator của họ thấy.
Bạn thử lại quá nhiều lần dẫn đến bị rate limit.
Error creating new order :: too many failed authorizations recently: see https://letsencrypt.org/docs/rate-limits/Let's Encrypt cho phép 5 lần xác thực thất bại trên mỗi hostname, mỗi account, mỗi giờ — kể từ khi họ thay đổi cơ chế rate-limit năm 2025, nó hoạt động theo kiểu "bucket" nạp lại, hồi lại khoảng một lần thử mỗi 12 phút — và việc cố đấm ăn răng với một firewall lỗi sẽ làm cạn kiệt nó rất nhanh. Chờ đợi có hiệu quả, nhưng cách sửa thực sự là thay đổi hành vi: sau bất kỳ lỗi nào, hãy debug với môi trường staging cho đến khi thành công.
sudo certbot certonly --apache --dry-run -d example.com -d www.example.comLưu ý certonly: --dry-run chỉ được chấp nhận bởi các subcommand certonly và renew, còn dạng certbot --apache --dry-run thuần túy sẽ từ chối chạy và báo lỗi --dry-run currently only works with the 'certonly' or 'renew' subcommands. Lệnh dry run xác thực với staging, nơi có giới hạn nới lỏng hơn và không cấp certificate thật, nên bạn có thể thất bại ở đó cả buổi chiều. Chỉ chạy lệnh thật khi staging đã pass. Các giới hạn khác — 50 certificate mỗi domain đã đăng ký mỗi tuần, 5 bản trùng lặp của cùng một tập hợp tên mỗi tuần — bạn sẽ chỉ gặp phải nếu có một script đang reissue trong một vòng lặp.
Khi HTTPS đã chạy, hãy nhớ rằng certificate chỉ bảo mật quá trình truyền tải, không phải server: port 22 vẫn đang nhận các nỗ lực đoán mật khẩu cả ngày. Kết hợp việc này với Fail2ban trên Ubuntu 24.04 là bước tiếp theo hợp lý.
FAQ
Tôi nên cài Certbot bằng snap hay apt cho Apache trên Ubuntu 24.04?
Hãy dùng apt. Ubuntu 24.04 đi kèm Certbot 2.9.0, phiên bản đủ mới cho mọi thứ trong hướng dẫn này, nhận được các bản patch bảo mật qua unattended-upgrades, và không yêu cầu snapd. Chỉ chọn snap nếu bạn cần bản release mới nhất ngay lập tức hoặc cần một DNS plugin chỉ được phân phối qua snap — và nếu bạn chuyển đổi, hãy dùng apt remove certbot python3-certbot-apache trước để hai bộ lập lịch gia hạn không tồn tại song song.
Tại sao Certbot báo "Unable to find a virtual host listening on port 80"?
Vì không có vhost port-80 nào đang enable có ServerName hoặc ServerAlias khớp với domain bạn truyền vào với -d — vhost mặc định của Ubuntu đi kèm với ServerName đã bị comment. Hãy chạy sudo apache2ctl -S, tìm (hoặc tạo) vhost sở hữu cái tên đó, thêm ServerName example.com, reload Apache, và chạy lại Certbot.
Làm thế nào để sửa lỗi "Timeout during connect (likely firewall problem)"?
Let's Encrypt không thể kết nối tới port 80 tại địa chỉ mà DNS của bạn công bố. Hãy kiểm tra firewall mạng ở cấp độ panel của nhà cung cấp cũng như ufw, xác nhận dig +short example.com trả về VPS này, và xóa hoặc sửa bất kỳ bản ghi AAAA cũ nào — quá trình xác thực ưu tiên IPv6 nếu có. Xác nhận việc sửa lỗi từ bên ngoài server bằng curl -I http://example.com, sau đó diễn tập với sudo certbot certonly --apache --dry-run -d example.com trước khi cấp phát thật.
Certbot có tự động gia hạn certificate trên Ubuntu 24.04 không?
Có. Package apt sẽ cài đặt certbot.timer, một systemd timer chạy hai lần mỗi ngày và gia hạn bất kỳ certificate nào còn dưới 30 ngày là hết hạn, sau đó reload Apache; bản snap sử dụng snap.certbot.renew.timer cho cùng một công việc. Hãy kiểm tra bằng systemctl list-timers certbot.timer và diễn tập với sudo certbot renew --dry-run — đừng tự thêm cron job của riêng bạn.
Làm thế nào để lấy wildcard certificate với Certbot và Apache?
Wildcard yêu cầu DNS-01 challenge: Certbot phải đặt một bản ghi TXT tại _acme-challenge.example.com, nghĩa là cần một plugin certbot-dns-* với API credentials cho DNS provider của bạn (phương án thay thế --manual yêu cầu phải tự tay sửa các bản ghi TXT tại mỗi lần gia hạn). Nếu bạn chỉ có một vài subdomain đã biết, một certificate SAN liệt kê rõ ràng chúng sẽ đơn giản hơn và không cần để API keys của DNS trên server.