所有部署指南 部署

如何在 Traefik (v3) 上安装自定义 SSL 证书

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 天):

  1. GetHTTPS(或你的续期工具)获取一张新证书。
  2. 就地覆盖文件:
    cp new-fullchain.pem ./traefik/certs/fullchain.pem
    cp new-privkey.pem   ./traefik/certs/privkey.pem
  3. 无需其他操作。 由于设置了 --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 证书,以实现更小、更快的握手。

相关文章

部署 2026-05-08
Docker 与反向代理的 SSL 证书配置
为 Docker 容器配置 HTTPS:使用 Nginx 反向代理、Traefik 自动获取 Let's Encrypt 证书,或手动挂载证书。
快速开始 2026-05-08
如何获取免费 SSL 证书(分步指南)
5 分钟从 Let's Encrypt 获取免费 SSL 证书 — 无需安装软件、无需注册账号。涵盖 4 种方法、两种验证方式、6 个平台的安装教程和故障排除。
部署 2026-05-08
如何在 Nginx 上安装 SSL 证书
在 Nginx 上安装 SSL 证书的分步指南。涵盖文件上传、完整 server 块配置、TLS 最佳实践、HTTP/2、HSTS、重定向设置、测试以及 6 个常见错误的排查方法。
SSL 与证书 2026-05-07
SSL 证书格式:PEM、PFX、DER 详解
理解 PEM、PFX/PKCS#12 和 DER 证书格式。了解你的服务器需要哪种格式,以及如何使用 OpenSSL 在它们之间转换。
在浏览器中获取免费 SSL 证书
无需安装,无需账号。私钥始终留在你的设备上。
获取证书