mkcert 本機 HTTPS 教學,產生憑證並驗證連線

mkcert 本機 HTTPS 教學:從安裝、根憑證信任到產生 localhost 與 IP 憑證,使用 Node.js 範例及 curl 驗證連線。依實測說明 CA 不受信任、SAN 名稱不符、手機連線與憑證清理,並區分根憑證和不可分享的私鑰。

用 AI 摘要這篇文章:

mkcert 能替 localhost 與 IP 位址產生開發用憑證。要讓本機 HTTPS 正常運作,還得把憑證接到伺服器,並讓使用的瀏覽器或程式信任簽發它的 CA。

下面用一個不需安裝 Express 的 Node.js 範例示範,成功時會回應 Local HTTPS OK。我以 mkcert v1.4.4、Node.js v26.7.0 在 macOS 測試,指定正確 CA 的連線通過;沒有提供信任,或改用憑證未涵蓋的名稱,則分別出現錯誤。這比只看到兩個 .pem 檔案,更能確認設定是否完成。

先確認你需要測的是 HTTPS 行為。MDN 說明 localhost 與回環位址可被視為可信來源,所以不能說本機 HTTP 一律無法使用 Service Worker 等 API。當你要測 TLS 連線、憑證驗證或 HTTPS 頁面載入資源的行為時,再建立這個環境。

安裝 mkcert,先理解根憑證的作用

mkcert 建立的 CA 是用來簽發開發憑證的本機憑證機構。執行 mkcert -install 會將它加入支援的信任庫;這是信任設定的變更,不只是安裝一個執行檔。共用或公司管理的電腦,先依管理規則處理。

依 mkcert 官方安裝說明,已有 Homebrew 的 macOS 可執行:

brew install mkcert

若需要透過 NSS 整合 Firefox,官方另列 brew install nss。Windows 可選已有的 Chocolatey 執行 choco install mkcert;使用 Scoop 則先 scoop bucket add extras,再 scoop install mkcert。Linux 的安裝方式依發行版而異,Ubuntu/Debian 的 NSS 工具套件是 libnss3-tools,mkcert 本體可依官方指引用 Homebrew、編譯原始碼或下載對應架構的執行檔。

這些是官方提供的安裝路徑;下方的實際連線結果來自 macOS。安裝完成後,開新終端機執行 mkcert -version,能顯示版本才繼續。如果出現找不到指令,先處理安裝或 PATH,不要直接複製後面的伺服器設定。

要讓這台電腦支援的信任庫接受本機 CA,執行:

mkcert -install
mkcert -CAROOT

第一行可能要求系統權限,請閱讀實際輸出的成功或失敗訊息;第二行只會顯示 CA 檔案位置。rootCA-key.pem 是根 CA 的私鑰,不可分享、上傳或提交到 Git。持有它的人能替信任此 CA 的用戶端簽出可被接受的憑證。

mkcert 官方安裝文件提醒不要分享 rootCA-key.pem,下方列出 macOS Homebrew 與 MacPorts 安裝方式Pin
mkcert 官方在安裝說明前提醒不要分享 rootCA-key.pem;下方列出 macOS 的安裝指令。

rootCA.pem 則是根憑證,與私鑰不同。需要讓另一台自己管理的測試裝置信任 CA 時,使用的是根憑證,不是 rootCA-key.pem。也不要因為檔案不含私鑰,就把陌生人提供的根憑證加入信任庫。

產生與網址相符的憑證,固定輸出檔名

先建立一個專用的空白測試資料夾,進入該資料夾後執行:

mkcert -cert-file localhost.pem -key-file localhost-key.pem localhost 127.0.0.1 ::1

這會在目前資料夾產生 localhost.pem 與 localhost-key.pem。前者是交給伺服器的網站憑證,後者是配對私鑰;檔名固定後,就不必猜多個名稱會產生哪個 localhost+N.pem。

指令最後三個值是憑證要涵蓋的名稱或 IP,不包含 https:// 或連接埠。本次產物的 SAN,也就是憑證列出的適用名稱,包含 localhost、127.0.0.1 及 ::1。換成其他名稱開啟,即使信任 CA,也不代表名稱驗證會通過。

若你使用 app.test,要把它加入簽發指令,並另外完成本機 hosts 或 DNS 解析。mkcert 不會幫你新增 hosts 紀錄,也不會啟動網站。萬用字元名稱應加引號,例如 "*.app.test";它也不會代替根名稱 app.test,有兩種需求就都列入。

網站私鑰也要保護。這個測試資料夾可放在 Git 專案外;若放進專案,先在 .gitignore 排除 localhost.pem 與 localhost-key.pem,再確認版本控制清單沒有這兩個檔案。已被 Git 追蹤的檔案不會因為補上忽略規則而自動消失。

接上 Node.js,確認 HTTPS 回應

若尚未安裝 Node.js,先到 Node.js 官方下載頁取得適合系統的版本。安裝後開新終端機執行 node --version,確認能顯示版本。接著在同一個測試資料夾將以下內容存成 server.mjs。這段使用 Node.js 內建 HTTPS server,不需要額外套件:

import https from 'node:https';
import fs from 'node:fs';

const server = https.createServer({
  key: fs.readFileSync('./localhost-key.pem'),
  cert: fs.readFileSync('./localhost.pem'),
}, (req, res) => {
  res.writeHead(200, { 'Content-Type': 'text/plain; charset=utf-8' });
  res.end('Local HTTPS OK\n');
});

server.listen(8443, '127.0.0.1', () => {
  console.log('Open https://127.0.0.1:8443');
});

在含有這三個檔案的資料夾執行 node server.mjs,保持終端機開著,再用同一台電腦的瀏覽器開啟 https://127.0.0.1:8443。預期顯示 Local HTTPS OK;同時確認沒有憑證錯誤,而不是按略過警告後只看頁面文字。

這個範例只監聽 127.0.0.1,因此不會讓區域網路內其他裝置直接連入。憑證雖然也包含 ::1,伺服器仍只在指定的 IPv4 位址監聽;憑證涵蓋某個 IP,和伺服器是否接受該 IP 的連線,是兩件事。

若要把「伺服器有沒有設定好」與「瀏覽器信任庫有沒有裝好」分開檢查,可在另一個終端機用 curl 明確指定 CA。macOS/Linux 的 shell 指令為:

curl --cacert "$(mkcert -CAROOT)/rootCA.pem" https://127.0.0.1:8443

Windows PowerShell 可將同一個根憑證位置傳給 curl.exe:

$caRoot = mkcert -CAROOT
curl.exe --cacert "$caRoot\rootCA.pem" https://127.0.0.1:8443

本次 curl 保持憑證驗證開啟,回傳 Local HTTPS OK。這代表這次指定 CA 的連線成功,不代表瀏覽器或其他裝置已信任 CA。我沒有為這次測試更動系統信任庫;上面的 -install 步驟是依官方安裝流程說明。

已有 Vite 專案時,將檔案內容放到 Vite 的 server.https 選項中的 key 與 cert。webpack-dev-server 則參考 目前的 devServer.server 設定,使用 type: 'https' 與 options.key、options.cert。不要直接沿用舊文章的 devServer.https。這些整合點依官方文件列出,本文的連線範例使用 Node.js。

出現錯誤時,先分辨是哪一層失敗

看到警告先保留完整錯誤名稱,不要立刻重做全部設定。下面幾種情況需要處理的地方不同:

看見的情況優先核對修正後如何確認
憑證簽發者不受信任、unable to verify這個用戶端是否信任簽發該網站憑證的 CA指定正確 CA 能通過,再核對該瀏覽器的信任設定
主機名稱不符、ERR_TLS_CERT_ALTNAME_INVALID開啟的名稱或 IP 是否列在 SAN為實際名稱重發憑證,替換檔案並重啟伺服器
ERR_CONNECTION_REFUSEDserver 是否還在執行、位址與 8443 是否一致回到終端機看錯誤,啟動後重開同一網址
ENOENT,找不到 pem 檔目前工作目錄與兩個檔名從正確資料夾啟動,不改用根 CA 私鑰頂替
EADDRINUSE,連接埠被占用8443 是否已有自己啟動的測試服務停止那個已確認的服務,或同步更換程式與網址的連接埠

我的 Node.js 測試在未指定 CA 時回報 UNABLE_TO_VERIFY_LEAF_SIGNATURE;加入正確 CA 後成功,但把驗證名稱改為 wrong.test,又回報 ERR_TLS_CERT_ALTNAME_INVALID。因此「已信任 CA」不能修復名稱不符。不同瀏覽器可能使用不同文案,仍應核對同一組條件。

Firefox 的情況也不能概括成永遠只讀獨立憑證庫。Mozilla 說明 Firefox 120 起可自動信任作業系統中安裝的第三方根憑證。若只有 Firefox 出錯,查看其憑證設定、組織政策及 mkcert 的 NSS 安裝訊息,必要時關閉並重新開啟瀏覽器,再測原網址。

如果是 Node.js 程式連不到本機 HTTPS,可對該次執行指定 NODE_EXTRA_CA_CERTS。它在程序啟動時讀取,執行中才修改變數不會生效。macOS/Linux 範例:

NODE_EXTRA_CA_CERTS="$(mkcert -CAROOT)/rootCA.pem" node client.mjs

這裡的 client.mjs 指你自己的 HTTPS 用戶端程式,不是前面的伺服器檔。若程式已明確設定 ca,Node.js 不會再套用這份額外 CA 清單;要查程式實際的 TLS 選項。不要用關閉憑證驗證作為永久修法。

若自訂名稱出現 ERR_NAME_NOT_RESOLVED,則先處理名稱解析,可參考 DNS 名稱解析錯誤的排查步驟。找不到主機時,重新簽發憑證無法代替 DNS 或 hosts 設定。

手機與其他電腦,需要各自完成連線與信任

手機上的 localhost 指手機本身,不是你的開發電腦。跨裝置測試時,先確定開發伺服器有在受控網路的適當位址監聽、裝置可以連到它,再把實際使用的區域網路 IP 或名稱放進憑證。前面的 Node.js 範例刻意只開在本機,不能直接拿手機連線。

需要共享信任時,只傳 rootCA.pem,絕對不傳 rootCA-key.pem。iPhone/iPad 手動安裝含憑證的描述檔後,Apple 還要求另外啟用 SSL/TLS 信任:在「設定 → 一般 → 關於本機 → 憑證信任設定」核對自己安裝的根憑證。單純完成描述檔安裝,不表示這一步也完成了。

Android 原生 App 也可能不信任使用者加入的 CA,需由開發者依 Network Security Configuration 的除錯 CA 設定處理。不要將「手機瀏覽器能開啟」推論成每個 App 都能連線。這裡提供平台文件與檢查順序,沒有宣稱已在手機實機重現。

正式公開網站請走公開 CA 的簽發與續期流程,不要要求一般訪客安裝你的本機根憑證。Let’s Encrypt 已在 2026 年 1 月開放 IP 位址憑證,因此「不支援 IP」不是選 mkcert 的理由;但 localhost 仍不是 Let’s Encrypt 可簽發的名稱。本機開發與對外服務要分開安排。

換發與清理時,分清檔案和信任設定

憑證過期、或新增測試名稱時,重新簽發並確認伺服器使用新檔案,再重啟服務。mkcert v1.4.4 沒有 -days 選項;本次執行會回報 flag provided but not defined: -days。要查看工具支援的參數,使用 mkcert -help,不要把其他憑證工具的參數混用。

停止範例時,在執行 node server.mjs 的終端機按 Ctrl+C,再開原網址確認已不再提供服務。若這台電腦不再需要信任該開發 CA,可在原本使用的 CAROOT 設定下執行:

mkcert -uninstall

這個指令移除支援信任庫中的本機 CA,不會刪除 CA 檔案,也不會自動撤銷所有已簽出的憑證。其他裝置若仍信任同一 CA,必須分別移除;為 Node.js 設定的額外 CA 路徑也要自行取消並重新啟動程式。

如果只是結束這份範例,確認伺服器已停止後,刪除自己建立的測試資料夾即可。CA 所在資料夾可能仍服務其他本機專案,不要把它一起當作範例檔案刪除。保留仍需使用的 CA 時,繼續保護根私鑰;懷疑根私鑰外洩時,則需移除受影響裝置的信任、建立新的 CA,再重新簽發與部署測試憑證。

Sliven 褚崇名
Sliven 褚崇名

每日分享科技新知、免費資源以及 WordPress、虛擬主機相關主題,任何問題歡迎在科技月球下方留言,或是發送 Email 至 [email protected] 與我聯繫。

文章: 1570

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *


Share to...