1. 自建服务入口的选型困境Traefik 与 Caddy 项目状态对比自建服务的入口层选型绕不开 Traefik 和 Caddy 这两个名字。它们都能做反向代理、自动申请证书、按域名分流但项目状态和演进节奏差别不小。我最近把一套内部工具从单机 Nginx 迁到容器编排环境顺手把 Traefik 和 Caddy 各跑了一遍重点看它们在“项目状态”这个维度上的表现——也就是社区活跃度、版本迭代、配置模型稳定性、以及遇到问题时能不能快速找到答案。先说结论方向Traefik 更像一个为动态环境而生的“服务网格入口”它原生对接 Docker、Kubernetes、Consul 等服务发现后端配置以标签和 CRD 为主适合服务频繁上下线的场景。Caddy 则更像一个“配置即文件”的现代 Web 服务器Caddyfile 写起来接近自然语言自动 HTTPS 是默认行为适合中小规模、配置相对稳定的入口。两者都在活跃维护但 Traefik 的版本迭代更快v3 之后对 Gateway API 的支持明显加强Caddy 的 v2 系列则保持较稳的配置语义升级时破坏性变更较少。这篇文章面向的是正在自建服务入口的开发者尤其是那些既想对比两套方案、又不想在 Key 管理上重复折腾的人。我会给出两套可复制的反向代理配置片段并用 TaoToken 统一 Key/API 通道完成一次请求验证确认路由与证书状态正常。这样你可以在同一套凭据体系下分别跑通 Traefik 和 Caddy再决定哪个更适合你的项目状态。需要提前说明的是本文不涉及任何网络访问方式的讨论只聚焦反向代理配置本身和 API 通道的验证。TaoToken 在这里的角色是统一模型调用的 Key 与 API 入口方便你在验证代理路由时有一个稳定的上游服务可打。官网入口是 https://taotoken.net/?utm_sourcetaotoken_aicg_blog_end API 地址是 https://taotoken.net/api 后面配置里会用到。如果你之前只用过 Nginx可能会觉得 Traefik 的标签配置有点绕Caddy 的 Caddyfile 又太“魔法”。这很正常。我的建议是先把两套最小可用配置跑起来用同一个上游 API 做验证再逐步加规则。下面从 TaoToken 的前置准备开始。2. TaoToken 前置准备统一 Key 与 API 通道在跑反向代理之前先把上游 API 通道准备好。TaoToken 在这里的作用是提供一个统一的 Key 和 API 地址让你在 Traefik 和 Caddy 里配置 upstream 时不用分别去对接不同厂商的凭据。你只需要在控制台创建一个 API Key然后把它写进代理配置的请求头或环境变量里。具体操作路径打开 https://taotoken.net/api-keys 登录后创建一个新的 Key。建议按项目命名比如traefik-caddy-test方便后面区分。创建完成后复制 Key它通常以sk-开头。这个 Key 就是后面两套配置里Authorization头的值。API 的基础地址是 https://taotoken.net/api 。注意这个地址不带任何查询参数直接作为 upstream 的 host 使用。如果你用的是 OpenAI 兼容的客户端通常还需要在路径上补/v1比如https://taotoken.net/api/v1/chat/completions。反向代理层一般只负责转发到https://taotoken.net路径由后端服务自己拼。为了验证代理是否正常工作我建议准备一个最简单的请求目标。比如用curl打一个模型列表接口或者用模型对话页面手动发一条消息。模型对话入口在 https://taotoken.net/models 你可以先在那里确认 Key 本身是有效的。如果模型对话能正常返回说明 Key 和 API 通道没问题接下来就可以专心调反向代理了。这里有一个容易踩的坑不要把 Key 直接硬编码在会被提交到 Git 的配置文件里。Traefik 的动态配置可以用环境变量注入Caddy 可以用{env.TAOTOKEN_KEY}这种占位符。后面配置片段里我会用环境变量方式你本地测试时可以先export再启动。另外如果你打算长期用这套入口做编码或 Agent 相关的事情可以了解一下 Coding Plan入口在 https://taotoken.net/coding-plan 。它和单次 API 调用是不同维度的东西这里不展开但值得知道有这么一个选项。前置准备做完后你应该手上有三样东西一个有效的 API Key、API 基础地址https://taotoken.net/api、以及一个能验证的请求目标。下面进入 Traefik 的配置。3. 可复制配置Traefik 动态路由与 Caddyfile 片段这一节给出两套配置。先看 Traefik。Traefik 的配置分静态和动态两部分。静态配置定义入口点和 provider动态配置定义路由和服务。我用 Docker provider 做示例这样服务上下线时 Traefik 能自动感知。静态配置traefik.ymlentryPoints: web: address: :80 websecure: address: :443 providers: docker: exposedByDefault: false file: filename: /etc/traefik/dynamic.yml watch: true certificatesResolvers: letsencrypt: acme: email: youexample.com storage: /acme.json httpChallenge: entryPoint: web动态配置dynamic.yml这里定义到 TaoToken 的路由http: routers: taotoken-router: rule: Host(api.local.test) entryPoints: - websecure service: taotoken-service tls: certResolver: letsencrypt services: taotoken-service: loadBalancer: servers: - url: https://taotoken.net passHostHeader: true注意passHostHeader: true这样上游收到的 Host 是api.local.test而不是taotoken.net。如果你希望上游看到原始 Host保持 true如果希望改写可以设为 false 并配合serversTransport。实测下来TaoToken 的 API 对 Host 不敏感两种都能通。再看 Caddy。Caddyfile 更紧凑api.local.test { reverse_proxy https://taotoken.net { header_up Host {upstream_hostport} header_up Authorization Bearer {env.TAOTOKEN_KEY} } tls youexample.com }这里header_up Authorization把 Key 注入到转发请求里。你也可以在客户端请求里带 AuthorizationCaddy 默认会透传。两种方式都行我倾向于在代理层注入这样后端服务不用各自管 Key。如果你用的是 Caddy 的 JSON 配置而不是 Caddyfile等价片段如下{ apps: { http: { servers: { srv0: { listen: [:443], routes: [ { match: [{host: [api.local.test]}], handle: [ { handler: reverse_proxy, upstreams: [{dial: taotoken.net:443}], headers: { request: { set: { Authorization: [Bearer {env.TAOTOKEN_KEY}] } } } } ] } ] } } } } }两套配置的共同点是都把api.local.test这个域名指向 TaoToken 的 API 地址都由代理层处理 TLS。区别在于 Traefik 用 YAML 加标签Caddy 用 Caddyfile 或 JSON。你可以把这两段直接复制到本地改一下邮箱和域名就能跑。配置里出现的TAOTOKEN_KEY环境变量记得在启动 Traefik 或 Caddy 之前 export。Traefik 本身不直接读这个变量它是给后端服务用的Caddy 的{env.TAOTOKEN_KEY}会实时读取。如果你在 Docker Compose 里跑可以这样写services: caddy: image: caddy:2 environment: - TAOTOKEN_KEY${TAOTOKEN_KEY} ports: - 80:80 - 443:443 volumes: - ./Caddyfile:/etc/caddy/CaddyfileTraefik 的 Compose 类似把dynamic.yml挂进去即可。配置写完后下一步是验证请求。4. 验证请求与成功结果路由与证书状态确认配置启动后先确认代理进程没有报错。Traefik 可以看日志里有没有Configuration loaded和证书申请成功的记录。Caddy 启动时会打印certificate obtained successfully。如果这两条出现了说明入口层基本正常。然后用curl打本地域名。因为api.local.test是本地解析你需要在/etc/hosts里加一行127.0.0.1 api.local.test。然后执行curl -v https://api.local.test/v1/models \ -H Authorization: Bearer $TAOTOKEN_KEY预期结果是返回一个 JSON里面包含模型列表。如果你看到HTTP/2 200和content-type: application/json说明路由和证书都正常。-v会打印 TLS 握手信息你可以确认证书的 CN 是api.local.test签发者是 Lets Encrypt 或 Caddy 的内部 CA本地测试时 Caddy 可能用内部 CA需要--insecure或信任根证书。Traefik 这边如果证书申请失败日志里会有acme: error。常见原因是 80 端口没通或者域名解析没指向本机。本地测试时你可以先用tls: {}自签或者用 Caddy 的tls internal。生产环境再换正式证书。验证通过后你可以进一步确认代理层有没有正确注入 Authorization。在 Caddy 的日志里加log指令或者在 Traefik 的 access log 里看请求头。更直接的办法是临时把 upstream 换成一个回显服务比如https://httpbin.org/headers看它收到的 Authorization 是不是你设置的值。确认后再换回 TaoToken。我实测下来Caddy 的header_up在 HTTPS upstream 下工作正常Traefik 的passHostHeader对 TaoToken 也没有影响。两套配置都能在 5 分钟内跑通。如果你在验证时遇到 502先检查 upstream 地址是不是写成了https://taotoken.net/api代理层通常只需要 host路径由后端拼。如果写成带/api的 URLTraefik 会把它当作路径前缀可能导致 404。证书状态方面Caddy 默认自动申请和续期Traefik 需要显式配置certificatesResolvers。如果你不想在本地折腾 ACME可以用tls internal或自签证书先验证路由逻辑再换正式证书。这一步不影响 API 通道的验证。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth跑这套配置时我遇到过几个典型报错这里逐个拆解。第一个是401 Unauthorized。这通常不是代理层的问题而是 Key 没传对。检查三点Key 是否以sk-开头、Authorization头是否是Bearer key格式、代理层有没有把该头覆盖掉。Caddy 的header_up如果写成header_up Authorization {env.TAOTOKEN_KEY}而漏了Bearer就会 401。Traefik 如果用了headers中间件做customRequestHeaders也要确认格式。第二个是local proxy failed。这个报错在 Caddy 里出现时通常是 upstream 不可达。检查dial地址是不是taotoken.net:443以及本机能不能解析和访问这个域名。如果你在容器里跑 Caddy容器内的 DNS 可能和宿主机不同需要确认网络模式。Traefik 的对应报错是dial tcp: lookup taotoken.net: no such host同样是 DNS 问题。第三个是reading choices相关的报错。这通常出现在流式响应场景代理层缓冲了 SSE 数据导致客户端读不到完整 chunk。Caddy 默认会 flush但如果你加了encode gzip可能会缓冲。解决办法是在reverse_proxy里加flush_interval -1。Traefik 则要确认没有开启 response buffering。这个报错不影响非流式请求但如果你用模型对话的流式输出就会遇到。第四个是OAuth相关。如果你在代理层后面跑的是需要 OAuth 的客户端比如某些 CLI 工具它们可能会自己发起 OAuth 流程而代理层改写了 Host 或路径导致回调失败。这时候要么把 OAuth 回调路径排除在代理规则外要么在代理层保留原始 Host。Traefik 的passHostHeader: true和 Caddy 的header_up Host {upstream_hostport}是两种不同策略按客户端要求选。还有一个容易忽略的点如果你同时跑了 Traefik 和 Caddy它们会抢 80/443 端口。测试时建议一次只跑一个或者给它们分配不同端口比如 Traefik 用 8080/8443Caddy 用 80/443。端口冲突的报错通常是bind: address already in use一看就懂。排障时优先看代理层日志再看上游返回。大部分问题出在配置格式和 DNS而不是 TaoToken 本身。如果你确认 Key 有效、模型对话能通那问题一定在代理配置。6. 语义一致 CTA按场景选择入口两套配置跑通后你可能会想进一步用这套入口做实际的事情。根据你的场景入口可以这样分如果你是在排障或接入阶段需要反复确认 Key 和 API 通道建议直接看 API Keys 页面和接入文档。API Keys 在 https://taotoken.net/api-keys 接入文档在 https://taotoken.net/doc 。这两个页面能帮你快速定位 401 和路径问题。如果你只是想验证模型是否可用或者手动发几条消息测试代理路由模型对话入口更直接https://taotoken.net/models 。在那里发一条消息如果返回正常说明整条链路是通的。如果你打算长期用这套入口做编码或 Agent 相关的工作Coding Plan 值得看一下https://taotoken.net/coding-plan 。它和单次 API 调用是不同维度的东西适合有持续调用需求的场景。最后提醒一句Traefik 和 Caddy 的项目状态都在活跃演进配置模型可能会随版本变化。你复制本文片段时注意核对当前版本的文档。TaoToken 的 API 地址和 Key 机制相对稳定但路径和模型列表建议以控制台和文档为准。把代理层和 API 通道分开验证出问题时就能快速定位是哪一层的事。