Ubuntu 24.04 Apache 安裝 Certbot 與 HTTPS
在 Ubuntu 24.04 上用一行命令為 Apache 申請免費 Let's Encrypt 憑證。跳過 snap,apt 提供 Certbot 2.9.0,並排查會阻擋申請的 ServerName 問題。
建置內容
在 Ubuntu 24.04 上建立 Apache 網站,透過 HTTPS 提供服務,使用由 Certbot 申請、受瀏覽器信任且免費的 Let’s Encrypt 憑證,並由 systemd timer 自動續期,之後不必再處理。實際執行工作的命令只有一行。所有問題都會在執行該命令前發生:vhost 缺少 ServerName、供應商防火牆封鎖 port 80,或 DNS 仍指向舊伺服器。因此,本指南大多用於確認前置條件,並列出每個錯誤所顯示的確切錯誤字串。
有兩點範圍說明。如果您的 web server 是 nginx,流程大致相同,但 plugin 與設定不同,請改用 nginx 版本的本指南。如果要保護的服務僅供內部使用,例如位於私有位址的管理面板,或沒有人從其他位置存取的 staging 主機,則完全不需要憑證授權單位;自簽憑證需要的設定較少,也能在離線環境中運作。
先決條件,以及 Certbot 執行前就會失敗的 3 種情況
- Apache 已透過純 HTTP 提供網站。 Certbot 的 Apache 外掛程式會修改現有網站,不會建立網站。如果你是從空白 VPS 開始,請先完成 Ubuntu 24.04 上的 LAMP stack,再回到這裡;本指南就是該流程缺少的 TLS 章節。
- 具備公開網域,且其 A record 指向 VPS 位址。 Let's Encrypt 的 HTTP-01 challenge 會讓驗證伺服器從網際網路連線到你的主機:沒有設定 port forwarding 的 NAT 家用實驗室無法使用,
.local名稱無法使用,裸 IP 也無法使用。dig +short example.com必須回傳你的 VPS 位址;如果你在過去 1 小時內修改過 DNS,請等候舊 record 的 TTL 到期後再申請。 - 如果存在 AAAA record,內容必須正確。 發布 AAAA record 時,Let's Encrypt 會優先使用 IPv6,因此過期或錯誤的 AAAA 會導致驗證失敗,即使你的筆電可能透過 IPv4 取得的
curl運作正常。請發布正確的 AAAA,或完全不要發布。
Port 80 和 443 必須同時在 ufw 及供應商的網路防火牆中開放;多數代管控制台都有第 2 層防火牆,作業系統完全無法看到。HTTP-01 會明確透過 port 80 進行驗證;不能只使用 443。
sudo ufw allow "Apache Full"
sudo ufw status完成這些設定後,整個流程只需 15 分鐘,其中 10 分鐘用來閱讀。
Snap 還是 apt 的 Certbot?在 24.04 上,apt 終於可以正常使用
Certbot 多年前改用 snap 發行,原因很充分:發行版套件已經停滯。Ubuntu 20.04 提供 Certbot 0.40,之後一直沒有更新,專案也不想再處理五年前的錯誤。在 24.04 上,這個原因已經不存在。套件庫提供 Certbot 2.9.0 這個新一代版本,而 unattended-upgrades 會持續為它提供修補程式。針對這個作業系統,我的建議是使用 apt。這樣可以省去 snapd daemon,Apache plugin 也會在同一筆交易中安裝,續期 timer 則會依照一般 Debian 方式整合至 systemd。
sudo apt update
sudo apt install -y certbot python3-certbot-apache
certbot --version正確結果:certbot 2.9.0。python3-certbot-apache package 是用來讀取及修改 Apache 設定的 plugin。若缺少它,certbot --apache 會因 The requested apache plugin does not appear to be installed 而失敗。
在以下兩種情況下,snap 仍然是正確選擇:你希望在最新 Certbot 發行當天就使用它,或需要只能透過 snap 發行的 DNS plugin(certbot-dns-* 的多個 provider plugin 都屬於此類)。如果選擇這個方式:
sudo apt remove -y certbot python3-certbot-apache
sudo snap install --classic certbot
sudo ln -s /snap/bin/certbot /usr/bin/certbot無論選擇哪一種方式,都不要同時執行兩者。兩個安裝會讓兩個續期排程器爭用 /etc/letsencrypt,而 shell 在 PATH 找到的 certbot,可能不是實際管理憑證的那個版本。上方的 apt remove 行不是可有可無的裝飾。
Certbot 編輯的 vhost 必須已經存在,ServerName 是關鍵
certbot --apache 會尋找連接埠 80 的 virtual host,確認其 ServerName 或 ServerAlias 是否符合你提供的每個 -d 網域,藉此驗證你對網域的控制權,然後建立該 vhost 的 SSL 副本。如果沒有符合的 ServerName,就不會匹配;而 Ubuntu 的預設 000-default.conf 中,ServerName 預設為註解。這行註解是本指南中那個大型指令失敗的最常見原因。
因此,在執行 Certbot 前,先為網站建立正確的 name-based vhost。建立 /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>啟用它,並確認 Apache 能解析設定,且會將該名稱路由至此 vhost:
sudo a2ensite example.com.conf
sudo apache2ctl configtest
sudo systemctl reload apache2
sudo apache2ctl -Sconfigtest 必須輸出 Syntax OK。如果同時輸出 AH00558: apache2: Could not reliably determine the server's fully qualified domain name,這表示全域 ServerName 有警告,不是你的 vhost 發生問題。在此處可安全忽略,並以 echo "ServerName $(hostname -f)" | sudo tee /etc/apache2/conf-available/servername.conf && sudo a2enconf servername && sudo systemctl reload apache2 消除。
-S 的輸出才是需要確認的項目。你應看到類似 port 80 namevhost example.com (/etc/apache2/sites-enabled/example.com.conf:1) 的行,且其下方有 alias www.example.com。Apache 回報的是實際讀取的 sites-enabled symlink,而不是你在 sites-available 編輯的檔案。如果 example.com 沒有列在連接埠 80 下,Certbot 也找不到它。
簽發憑證:certbot --apache
sudo certbot --apache -d example.com -d www.example.com首次執行時會詢問3項資訊:電子郵件地址(用於您的 ACME 帳戶及 CA 的緊急通知;Let's Encrypt 不再傳送到期警告,因此您必須自行監控續期作業)、是否同意 Let's Encrypt 條款,以及是否要與 EFF 分享您的電子郵件地址。現在不會再詢問重新導向設定:自 Certbot 2.0 起,Apache installer 預設會將 HTTP 重新導向至 HTTPS,這正是您需要的設定。只有在確實需要繼續透過純 HTTP 提供內容時,才傳入 --no-redirect。
成功時會顯示以下內容。請仔細閱讀,不要略過:
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在這段訊息背後,Certbot 執行了4項作業:如果 Apache 尚未啟用 ssl 模組,便先啟用該模組;寫入 example.com-le-ssl.conf,也就是您位於 *:443 的 vhost 複本,並加入 SSLEngine on 與憑證路徑;啟用該設定;最後在原本的 port-80 vhost 中加入 RewriteRule 區塊,將所有請求以 301 重新導向至 HTTPS。原始 vhost 檔案是經過編輯,而非取代;SSL 複本會放在旁邊,因此您可以查看 Certbot 加入的每一行設定。
憑證實際存放的位置,以及為什麼絕對不要複製它
所有檔案都會放在 /etc/letsencrypt/live/example.com/ 下:fullchain.pem(憑證與中繼憑證鏈,伺服器應指定使用此檔案)、privkey.pem(私密金鑰,僅限 root 讀取),以及供需要分開使用這些元件的軟體使用的 cert.pem 與 chain.pem。這些檔案都是指向 /etc/letsencrypt/archive/ 的符號連結,而這層間接指向正是續期機制:續期時會將新檔案寫入 archive/,再重新指向這些符號連結。讓其他軟體使用 live/ 路徑,它們就能自動取得續期後的檔案;若將檔案複製到其他位置,90 天後就會造成服務中斷。
另一個值得了解的檔案是 /etc/letsencrypt/renewal/example.com.conf。其中記錄此憑證的簽發方式、authenticator = apache、installer = apache 及網域,讓續期程序能在無人介入的情況下重複執行,包括之後重新載入 Apache。
續期已排程,請先驗證,不要重複建立
Let's Encrypt 憑證依設計可使用 90 days,而 apt package 已安裝必要的機制:systemd timer 每天在隨機時間執行 Certbot 兩次,並續期任何距離到期日 30 days 以內的憑證。不要另外新增 cron job;第二個排程器只會增加日誌雜訊,並提高觸發 rate limit 的風險。
systemctl list-timers certbot.timer
sudo certbot renew --dry-run第一個 command 會顯示 timer 目前為 active,NEXT 時間會落在接下來 24 hours 內。排程每天執行兩次,並加入隨機延遲,因此確切時間刻意設為不可預測(使用 snap install 時,timer 則為 snap.certbot.renew.timer)。dry run 會在 Let's Encrypt 的 staging environment 中完整模擬續期流程,使用真實 challenge,但不會簽發憑證,也不會消耗 rate limit。成功結果最後會顯示:
Congratulations, all simulated renewals succeeded:
/etc/letsencrypt/live/example.com/fullchain.pem (success)如果 dry run 失敗,約 60 days 後的實際續期也會以相同方式失敗。請立即修正,因為目前的憑證仍有完整的有效期限。最常見的原因是簽發憑證後新增了 firewall rule,導致 port 80 再次被封鎖。
使用 curl 驗證,以及鎖頭應顯示的內容
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 -dates第一個命令應回傳 HTTP/1.1 301 Moved Permanently,並包含 Location: https://example.com/ 標頭。這就是 Certbot 安裝的重新導向。第二個命令應回傳 HTTP/1.1 200 OK,且 curl 不應出現 TLS 錯誤。第三個命令會顯示簽發者、O = Let's Encrypt 行;其中 CN 應是類似 R12 或 E7 的簡短名稱,且 notAfter 約在 90 天後到期。在瀏覽器中應會看到鎖頭圖示,按一下後會顯示相同的簽發者。如果 curl 正常運作,但瀏覽器顯示警告,幾乎可以確定是載入了快取頁面或使用了錯誤的主機名稱,而不是憑證問題。
多個網站:使用單一 SAN 憑證,或每個網站各用一張憑證
兩種方式都可行,續期方式也相同。對於同一台伺服器上彼此無關的網站,請為每個網站各執行一次簽發命令。每個網站都會在 live/ 下取得自己的目錄與續期設定,因此其中一個網域發生問題時,不會阻止其他網域續期。這是我預設採用的方式。
對於包含多個名稱的單一網站,請將這些名稱放在同一張 SAN 憑證中。單張憑證最多可包含 100 個名稱。上方已使用 example.com 和 www.example.com 完成此設定。之後若要將名稱加入現有憑證,請指定憑證名稱重新簽發,並提供變更後的完整名稱清單:
sudo certbot --apache --cert-name example.com -d example.com -d www.example.com -d blog.example.comCertbot 會偵測網域集合已變更,要求你確認擴充作業,然後在原位置取代憑證,路徑仍為 live/,因此不需要修改其他設定。請注意,這份清單會取代原有內容,而不是附加內容:若從該命令中省略 www,新憑證會在無提示的情況下移除該名稱。
萬用字元憑證需要 DNS-01,而且通常不需要萬用字元
HTTP-01 無法簽發 *.example.com。在 Web 伺服器放置檔案只能證明你控制單一主機名稱,無法證明你控制整個命名空間。萬用字元憑證需要 DNS-01 challenge:Certbot 會在 _acme-challenge.example.com 建立 TXT 記錄。實務上,這表示要使用具備 DNS provider API 憑證的 certbot-dns-* plugin,或在每次續期時手動編輯 TXT 記錄並使用 --manual(這種方式非常麻煩,不應以此規劃)。從 TXT 記錄的運作方式,到可在無人介入的情況下自動續期的 plugin,完整說明請參閱 透過 DNS-01 使用 Certbot 申請萬用字元憑證。實際建議是:如果你已知有 4 個子網域,列出這 4 個名稱的 SAN 憑證比萬用字元憑證更簡單,也不需要將 DNS API key 存放在伺服器上。
失敗模式與你會看到的訊息
Certbot 無法啟動,因為 Apache 設定檔有誤。
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')外掛程式會先執行 configtest,確認 Apache 本身沒有問題後才會繼續;\ns 是字面內容,因為 Certbot 會輸出例外的 repr。請自行執行 sudo apache2ctl configtest:它會指出檔案與行號。常見原因包括手動編輯時的拼字錯誤、SSLCertificateFile 指向已不存在的路徑,或引用了尚未啟用的模組。修正問題,直到輸出 Syntax OK,再重新執行 Certbot。
沒有 vhost 符合該網域。
Unable to find a virtual host listening on port 80 which is currently the only challenge port.這是前文提到的缺少 ServerName 問題,會在申請憑證時被偵測到。Certbot 搜尋所有已啟用的 port-80 vhost,尋找符合你的 -d 的 ServerName/ServerAlias,但沒有找到。sudo apache2ctl -S 會顯示 Apache 實際將請求轉送到哪裡;請在正確的 vhost 中加入 ServerName,重新載入後再試。另一個類似問題是驗證請求到達了錯誤的 vhost。由於其他網站攔截請求,挑戰回應會是 Invalid response ... 404。診斷方式相同,使用的工具也是 apache2ctl -S。
驗證逾時。
Certbot failed to authenticate some domains (authenticator: apache).
...
Detail: ...: Timeout during connect (likely firewall problem)Let's Encrypt 無法從 DNS 公告的位址,對 port 80 建立 TCP 連線。可能性依序如下:供應商的網路防火牆(與 ufw 分開,需在主機代管控制台設定)、ufw 規則只允許 443 或只允許 SSH、DNS 仍指向先前的伺服器,或是過時的 AAAA 記錄問題:對方伺服器嘗試使用 IPv6,而你的伺服器只在 IPv4 回應。請從 VPS 外部測試:在筆電上執行 curl -I http://example.com,即可重現驗證伺服器看到的結果。
反覆重試後觸發速率限制。
Error creating new order :: too many failed authorizations recently: see https://letsencrypt.org/docs/rate-limits/Let's Encrypt 原本允許每個帳戶對每個主機名稱,在每小時內進行 5 次失敗驗證;自 2025 年速率限制重新調整後,改為可補充的配額,每 12 分鐘左右恢復 1 次重試。對故障中的防火牆持續重試,會很快耗盡配額。等待可以解決問題,但真正的修正方式是改變操作流程:每次失敗後,先使用 staging environment 進行除錯,直到驗證成功。
sudo certbot certonly --apache --dry-run -d example.com -d www.example.com請注意 certonly:只有 certonly 與 renew 子命令接受 --dry-run,直接使用 certbot --apache --dry-run 則完全不會執行,並會告訴你 --dry-run currently only works with the 'certonly' or 'renew' subcommands。dry run 會對 staging environment 進行驗證;該環境有較寬鬆的限制,也不會簽發實際憑證,因此你可以在那裡整個下午反覆失敗。只有在 staging environment 通過後,才重新執行正式指令。其他限制包括每個 registered domain 每週 50 張憑證,以及每週 5 次相同名稱集合的重複申請;除非某個 script 陷入迴圈並持續重新申請,否則通常不會遇到這些限制。
HTTPS 啟用後,請記住憑證保護的是傳輸,不是伺服器本身:port 22 仍然可能整天遭到密碼猜測。接著搭配 在 Ubuntu 24.04 上設定 Fail2ban,是很自然的下一個 30 分鐘工作。
FAQ
我應該在 Ubuntu 24.04 上使用 snap 還是 apt 為 Apache 安裝 Certbot?
使用 apt。Ubuntu 24.04 提供 Certbot 2.9.0,版本已足以支援本指南的所有內容,並透過 unattended-upgrades 取得安全性修補程式,也不需要 snapd。只有在您需要立即取得最新版本,或需要僅以 snap 發布的 DNS 外掛程式時,才選擇 snap;若要切換,請先執行 apt remove certbot python3-certbot-apache,避免兩個續期排程器同時存在。
為什麼 Certbot 顯示「找不到監聽 port 80 的 virtual host」?
因為沒有啟用的 port 80 vhost,其 ServerName 或 ServerAlias 與您透過 -d 傳入的網域相符。Ubuntu 的預設 vhost 將 ServerName 註解掉了。執行 sudo apache2ctl -S,找出或建立應負責該名稱的 vhost,加入 ServerName example.com,重新載入 Apache,然後再次執行 Certbot。
如何修正「連線期間逾時(可能是防火牆問題)」?
Let’s Encrypt 無法連線至 DNS 公開的位址上的 port 80。請同時檢查供應商控制面板層級的網路防火牆與 ufw,確認 dig +short example.com 回傳此 VPS,並刪除或修正過期的 AAAA 記錄;存在 IPv6 記錄時,驗證會優先使用 IPv6。使用 curl -I http://example.com 從伺服器外部確認修正結果,然後在正式簽發前以 sudo certbot certonly --apache --dry-run -d example.com 先行測試。
Certbot 會在 Ubuntu 24.04 上自動續期憑證嗎?
會。apt 套件會安裝 certbot.timer,這是 systemd timer,每日執行兩次,並為到期日剩下 30 天內的憑證續期,之後重新載入 Apache;snap 則使用 snap.certbot.renew.timer 執行相同工作。使用 systemctl list-timers certbot.timer 驗證設定,並以 sudo certbot renew --dry-run 先行測試。不要再另外建立 cron 工作。
如何使用 Certbot 和 Apache 取得 wildcard 憑證?
Wildcard 憑證需要 DNS-01 challenge:Certbot 必須在 _acme-challenge.example.com 建立 TXT 記錄,因此需要具備 DNS 供應商 API 憑證的 certbot-dns-* 外掛程式。(--manual 替代方案則需要在每次續期時手動編輯 TXT 記錄。)如果只有少數已知的子網域,明確列出這些網域的 SAN 憑證更簡單,也能避免將 DNS API 金鑰放在伺服器上。