Traefik 以透過 ACME 自動簽發憑證而聞名。但有時候你需要改用自己的憑證——例如由 GetHTTPS 簽發的憑證、企業/內部 CA 的憑證,或是你在別處管理的萬用字元(wildcard)憑證。本指南會在 Traefik v3 上,明確示範該怎麼做。
首先要理解的關鍵重點是:你無法用 Docker label 來掛載憑證檔案。 Traefik label 控制路由並啟用 TLS,但憑證本身必須由 file provider 以動態設定的形式提供。幾乎每個人第一次都會在這裡踩坑。我們會用正確的方式來做。
如果你還沒有憑證,5 分鐘免費取得一張。
何時使用此方法(相較於 Traefik 自動 ACME)
| 使用自動 ACME(Let’s Encrypt) | 使用你自己的憑證(本指南) |
|---|---|
| 公開網站,可從網際網路存取 | 內部/內網服務(無公開 DNS) |
| 標準 DV 憑證即可滿足需求 | 你需要特定 CA、萬用字元或 OV/EV 憑證 |
| Traefik 可完成 HTTP/DNS 驗證挑戰 | 實體隔離(air-gapped)或有防火牆阻擋的主機 |
| 你想要零憑證管理 | 你已經有一張來自 GetHTTPS 或其他來源的憑證 |
如果自動 ACME 適合你的情況,使用 Traefik 的 certificatesResolvers 會更簡單。本指南適用於「我已經有 fullchain.pem + privkey.pem,需要讓 Traefik 提供它們」的情境。
先決條件
- Traefik v3 以 Docker 容器執行(下方設定使用 Docker,但動態設定的部分適用於任何環境)
- 你的憑證檔案——共兩個:
fullchain.pem— 你的憑證 + 中繼信任鏈privkey.pem— 你的私鑰
- 一個指向該主機的網域(或供內部使用的本機 hosts 檔項目)
Traefik 需要哪些檔案? Traefik 在
certFile需要完整的信任鏈,在keyFile需要私鑰。如果你使用僅含葉憑證(leaf-only)的cert.pem,用戶端會收到「不完整的信任鏈」錯誤——Traefik 不會為 file-provider 憑證自動附加中繼憑證。請使用fullchain.pem。憑證格式說明 →
Traefik 的設定如何分割
Traefik 有兩種設定,而憑證屬於第二種:
- 靜態設定(Static configuration)——在啟動時設定(entrypoint、provider)。變更後需要重啟。這部分放在
traefik.yml或容器的命令列 flag。 - 動態設定(Dynamic configuration)——無需重啟即可即時重新載入(router、service、TLS 憑證)。這正是 file provider 所讀取的內容。
你的憑證屬於動態設定。這就是為什麼 label 無法承載它——label 由 Docker provider 讀取,但 tls.certificates 是 file-provider 的概念。
步驟 1:放置憑證檔案
把檔案放進你將掛載到容器內的目錄:
mkdir -p ./traefik/certs
cp fullchain.pem ./traefik/certs/
cp privkey.pem ./traefik/certs/
# 鎖定私鑰權限
chmod 600 ./traefik/certs/privkey.pem
chmod 644 ./traefik/certs/fullchain.pem
步驟 2:撰寫動態設定
建立 ./traefik/dynamic/certs.yml。這裡就是你宣告憑證的地方:
# ./traefik/dynamic/certs.yml (動態設定 — file provider)
tls:
certificates:
- certFile: /certs/fullchain.pem
keyFile: /certs/privkey.pem
# Optional: make this cert the default for any host that doesn't
# match a more specific certificate (e.g. a wildcard or internal CA).
stores:
default:
defaultCertificate:
certFile: /certs/fullchain.pem
keyFile: /certs/privkey.pem
這些路徑(/certs/...)是容器內的路徑——我們會在下一步掛載它們。Traefik 會透過 SNI 為每個請求挑選正確的憑證,將憑證的 SAN 與請求的主機名稱比對。當沒有其他憑證符合時,defaultCertificate 就是後備(fallback)。
步驟 3:設定 Traefik(靜態設定 + 掛載)
以下是一份完整的 docker-compose.yml。entrypoint 與 file provider 屬於靜態設定(以命令列 flag 傳入);憑證則從你剛寫好的動態檔案載入。
services:
traefik:
image: traefik:v3.3
container_name: traefik
restart: unless-stopped
command:
# ─── Entrypoints ───────────────────────────────
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
# Redirect all HTTP → HTTPS at the entrypoint level
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--entrypoints.web.http.redirections.entrypoint.permanent=true"
# ─── Providers ─────────────────────────────────
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
# File provider watches the dynamic config directory
- "--providers.file.directory=/dynamic"
- "--providers.file.watch=true"
ports:
- "80:80"
- "443:443"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "./traefik/certs:/certs:ro"
- "./traefik/dynamic:/dynamic:ro"
# ─── Example backend service ──────────────────────
whoami:
image: traefik/whoami
container_name: whoami
labels:
- "traefik.enable=true"
# Router: match the host, serve on the HTTPS entrypoint
- "traefik.http.routers.whoami.rule=Host(`example.com`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
# Enable TLS on this router — certificate comes from the
# dynamic config, NOT from a label.
- "traefik.http.routers.whoami.tls=true"
最關鍵的一行是 traefik.http.routers.whoami.tls=true。 它告訴 Traefik「以 TLS 提供此 router」。接著 Traefik 會透過 SNI,從 file-provider 的儲存區挑出相符的憑證。並沒有像 tls.certfile 這樣的 label——router 並不存在這種東西。
步驟 4:啟動並驗證
docker compose up -d
docker compose logs -f traefik
留意 file provider 載入你憑證的訊息。你應該不會看到 TLS 錯誤。然後進行驗證:
瀏覽器檢查
造訪 https://example.com。點擊鎖頭圖示:
- 簽發者(Issued by)——你的 CA(GetHTTPS 為 Let’s Encrypt,或你的內部 CA)
- 有效性(Valid)——起始與到期日期
- 網域(Domain)——與網址相符
命令列檢查
# Confirm Traefik is serving YOUR certificate, not its self-signed default
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null \
| openssl x509 -noout -subject -issuer -dates
如果你看到 CN=TRAEFIK DEFAULT CERT,表示 Traefik 退回使用其內建的佔位憑證——你的憑證並未載入。請參閱下方的疑難排解段落。
步驟 5(選用):強化 TLS
若要控制協定版本與加密套件,請在動態設定中加入一個 TLS options 區塊,並從 router 引用它。把以下內容加進 certs.yml:
tls:
options:
modern:
minVersion: VersionTLS12
sniStrict: true
然後在 router label 上:
- "traefik.http.routers.whoami.tls.options=modern@file"
@file 後綴告訴 Traefik 這組 option 來自 file provider。這會將你限制在 TLS 1.2 與 1.3,並拒絕沒有相符 SNI 的連線。
HSTS: 等你確信 HTTPS 在各處都能正常運作後,再透過 Traefik 的
headersmiddleware 加上Strict-Transport-Security標頭。HSTS 指南 →
如何續期
Traefik 不會自動續期你自行提供的憑證——這是使用自己憑證的取捨。當你的憑證接近到期時(Let’s Encrypt 為 90 天中的第 60 天):
- 從 GetHTTPS(或你的續期工具)取得一張新憑證。
- 就地覆寫檔案:
cp new-fullchain.pem ./traefik/certs/fullchain.pem cp new-privkey.pem ./traefik/certs/privkey.pem - 不需要其他動作。 因為已設定
--providers.file.watch=true,Traefik 會偵測到檔案變更並熱重載憑證——無需重啟、零停機。這是 Traefik 在處理自訂憑證時最棒的特性之一。
疑難排解
Traefik 提供「TRAEFIK DEFAULT CERT」而非我的憑證
原因: Traefik 無法載入你的憑證,因此退回使用其自簽的佔位憑證。幾乎都是路徑或掛載的問題。
解決方法:
# 1. Confirm the files exist INSIDE the container at the expected path
docker compose exec traefik ls -l /certs
# 2. Confirm the dynamic file is mounted and readable
docker compose exec traefik cat /dynamic/certs.yml
# 3. Check logs for the real reason
docker compose logs traefik | grep -i "certificate\|tls\|error"
certs.yml 中的 certFile/keyFile 路徑必須符合容器的路徑(/certs/...),而非你主機上的路徑。
「unable to find certificate for domain」/提供了錯誤的憑證
原因: 沒有任何憑證的 SAN 符合請求的主機名稱,而且也沒有設定 defaultCertificate。
解決方法: 要嘛把該主機名稱加進你的憑證,要嘛在 stores.default 區塊(步驟 2)設定一個 defaultCertificate,讓 Traefik 有後備可用。
Router 回傳 404,憑證根本沒被檢查
原因: router 規則不相符,或服務未被公開。在路由相符之前,TLS 都是無關緊要的。
解決方法:
# Verify the router exists and is enabled
docker compose exec traefik wget -qO- http://localhost:8080/api/http/routers 2>/dev/null | grep whoami
確認後端容器上有 traefik.enable=true,且 Host() 規則符合你正在測試的網域。
對 certs.yml 的變更未生效
原因: file provider 未在監看,或你編輯的是靜態設定(需要重啟)。
解決方法: 確認 --providers.file.watch=true 有在靜態設定中。請記住:entrypoint/provider 屬於靜態設定(需要重啟);router 與 tls.certificates 屬於動態設定(會熱重載)。
用戶端出現「x509: certificate signed by unknown authority」
原因: 信任鏈不完整——你在 certFile 使用了僅含葉憑證的 cert.pem,而非 fullchain.pem。
解決方法: 讓 certFile 指向完整的信任鏈。如果你只有各自獨立的檔案,請將它們串接起來(葉憑證在前,接著是中繼憑證):
cat cert.pem intermediate.pem > fullchain.pem
常見問題
我可以用 Docker label 指定憑證檔案嗎?
不行。 這是第一大誤解。label 設定 traefik.http.routers.<name>.tls=true 來啟用 TLS,但憑證檔案必須透過 file provider 在動態設定中宣告(tls.certificates)。並沒有任何 router label 能指向 .pem 檔案。
如果我已經有憑證,router 上還需要 tls=true 嗎?
需要。在 tls.certificates 中宣告憑證會讓它可被使用,但每個 router 仍需要 tls=true(或監聽一個已啟用 TLS 的 entrypoint)才能真正以 HTTPS 提供服務。光是定義憑證並不會把 router 切換到 TLS。
我可以混用自動 ACME 與自訂憑證嗎?
可以。Traefik 可以為某些 router 使用 certificatesResolver,同時為其他 router 使用 file-provider 憑證。就單一請求而言,透過 SNI 明確相符的 tls.certificates 對該主機名稱的優先順序高於 ACME。
在 Kubernetes 上憑證放在哪裡?
不是用 file provider——在 Kubernetes 上,你以 TLS Secrets 形式提供憑證,並從 IngressRoute/Ingress 引用它們。本指南中的 file-provider 做法適用於 Docker 與獨立(非 K8s)環境。
Traefik 會自動續期我的自訂憑證嗎?
不會。自動續期僅適用於 Traefik 自己透過 ACME 簽發的憑證。對於你自己的憑證,你需要替換檔案——但多虧了 --providers.file.watch=true,Traefik 會熱重載它們並達到零停機,如上方如何續期段落所述。
這對 ECDSA/ECC 憑證也適用嗎?
適用。Traefik 提供 ECDSA 憑證的方式與 RSA 完全相同——一樣的 certFile/keyFile 設定。GetHTTPS 預設簽發 ECDSA P-256,以獲得更小、更快的交握。