你配了通配 DNS:*.example.com → 10.0.0.1。foo.example.com 正常解析。但 a.b.example.com(更深的子域)返回 NXDOMAIN。这不是 bug,而是规则:DNS 通配只匹配一个 label,从不一次匹配一串。*.example.com 覆盖 foo.example.com,但不覆盖 foo.bar.example.com。这由 RFC 4592 定义,而且关键是——通配连自己的子域都不覆盖:*.example.com 不会匹配任何位于 *.example.com 之下的名字。
最快修复: 每多一层需要覆盖,就为那个确切的父级再加一条通配。要让 *.bar.example.com 生效,就在 *.example.com 之外再建一条 *.bar.example.com 记录。如果你在 Cloudflare 上开了代理(橙云),第一层以下的通配还需要 Advanced Certificate Manager,边缘证书才真正覆盖它(下文细说)。如果子域是有限且已知的列表,干脆别用通配,直接加显式 A/CNAME 记录。
dig +short foo.example.com # 解析成功 -> 第一层通配生效
dig +short foo.bar.example.com # NXDOMAIN -> bar.example.com 这一层没有通配
先判断你属于哪一类
| 症状 | 最可能的原因 | 跳转 |
|---|---|---|
| 单层子域解析,多层 NXDOMAIN | 通配只覆盖一个 label | 原因 1,修复 1-3 步 |
| DNS 解析但浏览器报证书错 | 证书 SAN 不含更深的名字 | 原因 4,修复第 5 步 |
| DNS-only 正常,开代理就坏(Cloudflare) | 边缘证书不覆盖嵌套通配 | 原因 5 |
| 部分用户失败,部分正常 | 这一层缺 AAAA(IPv6) | 原因 6 |
| 一个 resolver 解析、另一个不解析 | 缓存陈旧 / 尚未生效 | 修复第 6 步 |
常见原因
按命中率从高到低。
1. 通配只覆盖一个 label
*.example.com 匹配:
foo.example.com✓bar.example.com✓
但不匹配:
foo.bar.example.com✗a.b.c.example.com✗
按 RFC 4592,星号只替代单个 label,并且通配会”挡住自己”——它永远不会为树中位于自己之下的名字合成应答。
怎么判断:单 label 子域能用,再深一点就返回 NXDOMAIN 或错的值。
dig +short foo.example.com # 解析
dig +short foo.bar.example.com # NXDOMAIN 或错的值
2. 每一层都需要自己的通配
要覆盖更深的层,就加一条父级正好是那一层的通配:
*.example.com A 10.0.0.1
*.bar.example.com A 10.0.0.1
*.bar.example.com 覆盖 x.bar.example.com,但同样不覆盖 x.y.bar.example.com。如果你真的需要每一个深度,就得沿每条分支一直往下加通配——这正是为什么拉平层级(修复第 4 步)通常是更好的答案。
怎么判断:多租户 app 客户能创建嵌套子域,但你只加了最顶层那条通配。
3. 不同提供商差在记录管理,不在匹配规则
通配的匹配算法由 RFC 4592 标准化,合规的 resolver 在哪都一样。真正有差异的是提供商工具:有的面板允许你直接填 *.bar,有的要求先建好中间区域,有的禁止在代理/CDN 记录上用通配。如果一条深层通配在一家提供商”能用”、另一家不行,几乎总是因为能用的那边有一条你忘了加的显式记录——而不是匹配规则变了。
怎么判断:从两家导出 zone 文件做 diff。能用的那份 zone 会多一条通配或显式记录。
4. SSL 证书不覆盖更深的名字
哪怕 DNS 解析了,通配 TLS 证书也只覆盖一个 label。Let’s Encrypt 的 *.example.com 证书验证 foo.example.com,但不验证 foo.bar.example.com——后者需要单独的 *.bar.example.com 证书。截至 2026 年 6 月,Let’s Encrypt 签发通配仍然要走 ACME 的 DNS-01 challenge(HTTP-01 无法签发通配)。
怎么判断:DNS 解析,但浏览器报 NET::ERR_CERT_COMMON_NAME_INVALID。确认证书的 SAN 列表:
echo | openssl s_client -connect foo.bar.example.com:443 -servername foo.bar.example.com 2>/dev/null \
| openssl x509 -noout -text | grep -A1 "Subject Alternative Name"
如果 SAN 只显示 *.example.com,那更深的名字就没被覆盖。
5. Cloudflare 代理不覆盖嵌套通配
Cloudflare 明确说”wildcards are only supported on the first label”(通配只支持第一层 label)。你可以代理更深的通配——在 Name 字段填 *.www 来覆盖 *.www.example.com——但坑在证书。Cloudflare 的 Universal SSL 只覆盖根域和第一层子域。要代理第一层以下的通配(*.www.example.com),你需要 Advanced Certificate Manager,边缘才有匹配的证书;否则即便 DNS 解析,橙云请求也会 TLS 失败。
怎么判断:DNS-only(灰云)下能用,一开代理就抛 525/握手错误或证书告警。
6. 缺 IPv6 的 AAAA 记录
你加了 *.example.com A 但没加 AAAA。偏好 IPv6 的客户端在更深的子域上失败,纯 IPv4 客户端则正常。
怎么判断:部分用户能用、部分不能。
dig +short AAAA foo.bar.example.com # A 记录解析、AAAA 为空
最短修复路径
第 1 步:摸清子域到底有几层
foo.example.com → 1 层
bar.foo.example.com → 2 层
baz.bar.foo.example.com → 3 层
每一层想覆盖,都需要它自己的通配或显式记录。
第 2 步:每层加一条通配
*.example.com A 10.0.0.1
*.foo.example.com A 10.0.0.1
*.bar.foo.example.com A 10.0.0.1
繁琐,但严格遵守”一个 label”规则。多数面板里 Name 字段只填左半部分(*、*.foo、*.bar.foo),区域会补上其余部分。
第 3 步:或给已知子域加显式记录
如果集合有限(不是用户生成),就枚举它——没有通配歧义,SSL 也更简单:
foo.example.com A 10.0.0.1
bar.foo.example.com A 10.0.0.1
baz.foo.example.com A 10.0.0.1
第 4 步:多租户 app 拉平层级
有 customer.team.app.example.com 这种就重构成 customer-team.example.com。一条单 label 通配(*.example.com)就覆盖所有租户,一张通配证书也全覆盖。对用户生成的子域,这是最干净的长期方案。
第 5 步:拿到匹配每一层的证书
跨多层的 TLS 需要下面之一:
- 多张通配证书(
*.example.com和*.foo.example.com),每张都用 Let’s Encrypt 的 DNS-01 challenge 签发。 - 一张 multi-SAN 证书,显式列出每一层。
- 在 Cloudflare 上,用 Advanced Certificate Manager 覆盖第一层以下的代理通配。
签发后验证:
echo | openssl s_client -connect baz.bar.foo.example.com:443 \
-servername baz.bar.foo.example.com 2>/dev/null \
| openssl x509 -noout -text | grep -A1 "Subject Alternative Name"
第 6 步:用多个 resolver 测
for resolver in 8.8.8.8 1.1.1.1; do
for sub in foo bar.foo baz.bar.foo; do
echo -n "$sub.example.com @$resolver: "
dig @$resolver +short "$sub.example.com"
done
done
每行都应返回你的 IP。空行说明那一层仍没有通配或记录(或者改动还没生效——按父区域的 TTL 等一等,通常 300-3600 秒)。
怎么确认已修好
dig +short a.b.example.com在至少两个 resolver(8.8.8.8、1.1.1.1)上都返回你的 IP。curl -sI https://a.b.example.com返回 HTTP 状态码(而不是连接或 TLS 错误)。openssl s_client的 SAN 检查列出一条能覆盖a.b.example.com的通配。- 如果在 Cloudflare 上代理,同一条
curl在橙云开启时也能成功,而不只是灰云。
常见问答
为什么 *.example.com 对 foo.example.com 生效、对 foo.bar.example.com 不生效?
通配只替代单个 label(RFC 4592)。foo 是一个 label,所以命中;foo.bar 在通配之下有两个 label,没有可合成的东西,于是返回 NXDOMAIN。
*.example.com 是不是覆盖 bar.example.com 以及它下面的所有内容?
不是。它覆盖 bar.example.com 这种直接子级,但不覆盖 x.bar.example.com。通配从不匹配位于自己之下的名字——那一层需要单独的 *.bar.example.com 记录。
一张通配 SSL 证书能不能覆盖所有深度?
不能。*.example.com 证书只覆盖 foo.example.com。每深一层(*.bar.example.com)都需要它自己的通配证书,或一张列出这些名字的 multi-SAN 证书。Let’s Encrypt 通配必须走 DNS-01 challenge。
嵌套通配不开代理时正常,挂到 Cloudflare 后面就坏,为什么?
Universal SSL 只覆盖根域和第一层子域。第一层以下的代理通配(*.www.example.com)需要 Advanced Certificate Manager,边缘才会出示匹配的证书。
有没有办法不用一层一层维护通配?
有——拉平层级。把 team.customer.example.com 换成 team-customer.example.com,这样一条 *.example.com 记录加一张证书就全覆盖。对有限列表,显式记录也比嵌套通配更简单。
预防建议
- 设计子域层级时记住”一个 label”规则;尽量控制在
<= 2层。 - 多租户 app 用单 label 子域(
customer-team.example.com,而不是team.customer.example.com)。 - 每个通配层都要同步规划好对应的 SSL 策略。
- 加新子域层时,把 DNS、证书 SAN、CDN/代理配置一起审一遍。
- 在 DNS 笔记里记下通配结构,免得后来的工程师踩坑。
相关阅读
标签: #排查