Caddy JSON 配了多个域名却串站:检查 match 数组的 OR 关系
Caddy 原生 JSON 的多域名站点返回同一内容,检查 match 数组的 OR 条件、同集合 AND 关系和 terminal 的位置,再核对实际入口与生效配置。
同一台 Caddy 用原生 JSON 配置了多个域名,访问第二个域名仍返回第一个站点的内容。HTTPS 已经正常建立连接时,先看 HTTP 路由里的 match,尤其是把 host 和 path 分成两个数组元素的写法。给两个域名重新分配证书,未必会改变这条路由的匹配结果。
以下针对 Caddy 2 的原生 JSON HTTP 路由。Caddyfile 还有指令适配和排序规则,不能仅凭 Caddyfile 表面顺序推断最终 JSON 中的路由结构。
两个数组元素表示任选一个匹配
下面是容易导致串站的路由片段,省略了处理器,不是完整配置:
{
"match": [
{ "host": ["api.example.com"] },
{ "path": ["*"] }
]
}
一个 match 数组中的各个对象是不同的匹配器集合,集合之间按 OR 判断。上面的条件相当于“域名是 api.example.com,或者路径匹配 *”。Caddy 的 * 路径匹配所有请求路径,所以另一个域名也能进入这条路由。
如果这条路由排在前面,并由直接响应或反向代理处理了请求,后面的站点路由可能没有机会提供响应。现象看起来像证书选错或后端缓存,实际却是第一条路由的条件过宽。
同时要求域名和路径时,放在同一个对象
同一匹配器集合内的不同匹配器按 AND 判断。需要域名与路径同时满足时,可以写成下面的结构:
{
"match": [
{
"host": ["api.example.com"],
"path": ["/v1/*"]
}
]
}
如果整个域名都交给这条站点路由,直接保留 host 条件即可:
{
"match": [
{ "host": ["api.example.com"] }
]
}
host 列表中的多个域名用于让这个主机匹配器接受任一所列主机。不要因此把“一个集合中的 host 与 path 同时满足”和“match 数组中的两个集合任选一个”混为一谈。
terminal 控制后续路由,不能补救过宽条件
当某个站点分支应独立完成请求,不再进入该层后续路由时,可以评估是否为该路由设置 terminal: true。它影响匹配之后的路由链,并不会把上面的 OR 自动改成 AND。
先修正域名条件,再决定终止位置。有些配置让前面的路由设置变量、执行认证或补充请求头,再交给后续路由处理;直接加 terminal 可能切断这些流程。直接响应和反向代理等处理器本身也可能结束正常处理链,不能认为所有多站点配置都必须加这个字段。
同时检查无 match 的兜底路由和通配域名路由。它们若放在前面并直接提供响应,同样可能吞掉更具体的站点。原生 JSON 的路由顺序与处理链需要一起判断,不能只逐条确认每个域名都在配置文件中出现过。
核对实际加载配置与请求身份
检查正在运行的实例使用的配置、HTTP server、监听入口和路由顺序,确认没有改到另一份文件或另一个容器。通过现有受保护的管理方式读取配置时,不应为了排查而把管理 API 暴露到公网;配置副本也可能包含后端凭据,需要限制访问。
再核对请求的域名、HTTP Host、路径和实际入口。浏览器正常按域名访问时,TLS 身份选择与 HTTP 路由通常使用相关的域名信息,但它们属于不同阶段。证书报错、请求进入了另一台服务器、代理改写了 Host,应分别调查,不能都归到这个 match 写法上。
若修正匹配后,第二个域名已经进入正确路由,后端仍返回相同内容,就继续检查站点上游、应用虚拟主机和应用自身配置,不要重复调整证书加载目录。
变更后检查正常域名与兜底请求
保存旧配置,在本机或现有隔离环境中检查 JSON 与当前 Caddy 版本的配置校验结果,再按部署流程加载。域名、路径、认证及原有兜底行为都要保留,避免只修首页串站,却把其他受保护路径交给错误处理器。
分别访问各个配置域名、目标路径、域名正确但路径不匹配的请求,以及不应被任何站点接受的域名。成功标准是每项请求进入预定路由并返回对应内容,而不只是 TLS 握手成功。加载失败或出现新的路由异常时,恢复保存的配置;恢复后重新核对实际生效的实例与路由。


