Traefik 以通过 ACME 自动签发证书而闻名。但有时你需要改用自己的证书 —— 由 GetHTTPS 签发的证书、企业/内部 CA 签发的证书,或是你在别处管理的通配符证书。本指南将精确演示如何在 Traefik v3 上完成这件事。
需要预先理解的关键点是:你无法用 Docker 标签来挂载证书文件。 Traefik 标签控制路由并启用 TLS,但证书本身必须作为动态配置由 file provider 提供。几乎每个人第一次都会在这里栽跟头。我们会用正确的方式来做。
如果你还没有证书,5 分钟免费获取一张。
何时使用本方案(相比 Traefik 的自动 ACME)
| 使用自动 ACME(Let’s Encrypt) | 使用自己的证书(本指南) |
|---|---|
| 面向公网、可从互联网访问的网站 | 内部/内网服务(无公网 DNS) |
| 标准 DV 证书即可满足 | 你需要特定 CA、通配符或 OV/EV 证书 |
| Traefik 能完成 HTTP/DNS 质询 | 物理隔离或受防火墙限制的主机 |
| 你想要零证书管理 | 你已从 GetHTTPS 或别处拿到证书 |
如果自动 ACME 适合你的场景,Traefik 的 certificatesResolvers 会更简单。本指南面向的是”我已经有 fullchain.pem + privkey.pem,需要让 Traefik 提供它们”这种情况。
前置条件
- Traefik v3 以 Docker 容器方式运行(下面的配置使用 Docker,但动态配置那部分适用于任何部署方式)
- 你的证书文件 —— 共两个:
fullchain.pem—— 你的证书 + 中间证书链privkey.pem—— 你的私钥
- 一个指向该主机的域名(内部使用时也可用本地 hosts 文件条目)
Traefik 需要哪些文件? Traefik 需要在
certFile中放完整证书链,在keyFile中放私钥。如果你使用只含叶子证书的cert.pem,客户端会收到”证书链不完整”的错误 —— 对于 file provider 的证书,Traefik 不会自动补全中间证书。请使用fullchain.pem。证书格式详解 →
Traefik 的配置是如何拆分的
Traefik 有两种配置,而证书属于第二种:
- 静态配置(Static configuration) —— 在启动时设定(entrypoint、provider)。修改它需要重启。这部分写在
traefik.yml或容器命令行参数中。 - 动态配置(Dynamic configuration) —— 无需重启即可实时重载(router、service、TLS 证书)。这正是 file provider 读取的内容。
你的证书属于动态配置。这就是为什么标签无法承载它 —— 标签由 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
# 可选:把这张证书设为默认证书,用于任何无法匹配到
# 更具体证书的主机(例如通配符证书或内部 CA 证书)。
stores:
default:
defaultCertificate:
certFile: /certs/fullchain.pem
keyFile: /certs/privkey.pem
这些路径(/certs/...)是容器内部的路径 —— 我们将在下一步挂载它们。Traefik 会通过 SNI 为每个请求选择正确的证书,用证书的 SAN 去匹配请求的主机名。defaultCertificate 是在没有其他证书匹配时的兜底选项。
步骤 3:配置 Traefik(静态配置 + 挂载)
下面是一份完整的 docker-compose.yml。entrypoint 和 file provider 属于静态配置(以命令行参数传入);证书则从你刚写好的动态文件加载。
services:
traefik:
image: traefik:v3.3
container_name: traefik
restart: unless-stopped
command:
# ─── Entrypoints ───────────────────────────────
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
# 在 entrypoint 层面把所有 HTTP → HTTPS 重定向
- "--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 监视动态配置目录
- "--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"
# ─── 示例后端服务 ──────────────────────
whoami:
image: traefik/whoami
container_name: whoami
labels:
- "traefik.enable=true"
# Router:匹配主机,在 HTTPS entrypoint 上提供服务
- "traefik.http.routers.whoami.rule=Host(`example.com`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
# 在此 router 上启用 TLS —— 证书来自动态配置,
# 而非来自标签。
- "traefik.http.routers.whoami.tls=true"
关键的一行是 traefik.http.routers.whoami.tls=true。 它告诉 Traefik”通过 TLS 提供此 router 的服务”。随后 Traefik 会通过 SNI 从 file provider 的存储中挑选匹配的证书。没有 tls.certfile 这样的标签 —— 对 router 而言它并不存在。
步骤 4:启动并验证
docker compose up -d
docker compose logs -f traefik
留意 file provider 加载你证书的日志。你应当不会看到 TLS 错误。然后进行验证:
浏览器检查
访问 https://example.com。点击锁形图标:
- 签发者 —— 你的 CA(GetHTTPS 用的是 Let’s Encrypt,或是你的内部 CA)
- 有效期 —— 起始与到期日期
- 域名 —— 与 URL 一致
命令行检查
# 确认 Traefik 提供的是你的证书,而非它的自签名默认证书
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 标签上:
- "traefik.http.routers.whoami.tls.options=modern@file"
@file 后缀告诉 Traefik 这组 option 来自 file provider。这会将你限制为仅使用 TLS 1.2 和 1.3,并拒绝没有匹配 SNI 的连接。
HSTS: 等你确信 HTTPS 已在各处正常工作后,再通过 Traefik 的
headers中间件添加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. 确认文件在容器内部的预期路径上确实存在
docker compose exec traefik ls -l /certs
# 2. 确认动态文件已挂载且可读
docker compose exec traefik cat /dynamic/certs.yml
# 3. 检查日志找出真正的原因
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 无关紧要。
解决:
# 验证 router 存在且已启用
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 标签指定证书文件吗?
不能。 这是头号误解。标签用 traefik.http.routers.<name>.tls=true 来启用 TLS,但证书文件必须通过 file provider 在动态配置中声明(tls.certificates)。不存在任何指向 .pem 文件的 router 标签。
如果我已经有证书,还需要在 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 证书,以实现更小、更快的握手。