Cloudflare SaaS证书签发失败踩坑记录封面图

Cloudflare SaaS证书签发失败踩坑记录

闲来没事上Cloudflare看看网站的运行状况,结果发现我网站根域名的证书准备就要过期了,而Cloudflare上证书一直签发不下了,卡“待验证”
01.png
我感觉不太对劲,于是开始排查问题。

故障排查

我第一反应,就是去直接抓取这个验证的URL看看是怎么回事,结果:

NekoHost-HK [~]# curl http://nekopara.uk/.well-known/acme-challenge/mRgQjOrYbvQGgoq6Rz4F01gDjbbyQTuwWyDdTz3nfhrn07RRSpFhG_TQVFmUxq9i
<html>
<head><title>301 Moved Permanently</title></head>
<body>
<center><h1>301 Moved Permanently</h1></center>
<hr><center>cloudflare</center>
</body>
</html>

嗯?怎么被301重定向了?这是证书验证目录啊?
带着疑惑,我问了ChatGPT,它甚至调用工具去实际验证了链接的情况,然后说这大概率是我重定向规则配置的问题,而不是Cloudflare的BUG,建议我排查重定向规则:

我实际检查你给出的 URL 时,它会从:
http://nekopara.uk/.well-known/acme-challenge/...
跳到:
https://www.nekopara.uk/.well-known/acme-challenge/...
这正好解释了为什么一直停在“待验证”。你现在拿到的是 301 Moved Permanently,而 Cloudflare/GTS 期望在原始验证 URL 上拿到 200 OK,正文就是页面给你的这一整串验证响应。Cloudflare 的当前排障文档也明确写了:/.well-known/* 验证路径不要发生重定向;Redirect Rules 应把这个路径排除。
你现在最该检查的是 Cloudflare 里有没有类似“nekopara.uk → www.nekopara.uk”的
Redirect Rule、Bulk Redirect、旧 Page Rule 或 Worker。特别是这种规则:
nekopara.uk/* → https://www.nekopara.uk/$1
它会连 ACME challenge 一起重定向。

嗯?把http验证请求也重定向了?我记得之前用NameSever接入Cloudflare的时候配置,也没有遇到过证书签发失败这个问题。

随后,我按照建议,把跟域名重定向到www重定向规则的匹配条件从
(http.host eq "nekopara.uk")
改成
(http.host eq "nekopara.uk")and not starts_with(http.request.uri.path, "/.well-known/")
保存配置,再次尝试抓取验证URL,这次就成功了:

NekoHost-HK [~]# curl https://nekopara.uk/.well-known/acme-challenge/mRgQjOrYbvQGgoq6Rz4F01gDjbbyQTuwWyDdTz3nfhrn07RRSpFhG_TQVFmUxq9i
mRgQjOrYbvQGgoq6Rz4F01gDjbbyQTuwWyDdTz3nfhrn07RRSpFhG_TQVFmUxq9i.r54qAqCZSs4xyyeamMffaxyR1FWYVb5OvwUh8EcrhpI

过了一会,Cloudflare也验证上了:
02.png

为什么会这样?

说实话,解决完问题后我反而更纳闷了——明明以前用 NS 接入 Cloudflare 的时候,我也是这么配的 nekopara.uk/* → https://www.nekopara.uk/* 重定向,为什么那时候证书就从来没出过问题?
带着这个疑问,我询问了ChatGPT,才发现自己不经意间掉进了一个认知误区。这里真正的区别不是“同一个 Cloudflare 在不同接入方式下行为随机”,而是我实际上走了两套完全不同的证书/DCV 体系。

NS 全托管时代:DNS DCV

以前我把域名 NS 直接托管给 Cloudflare,属于 Cloudflare 的 Full DNS Setup。此时 Cloudflare 本身就是这个域名的权威 DNS。对于这种模式下的 Universal SSL,Cloudflare 官方明确说明:DCV 会由 Cloudflare 自动通过 DNS TXT 完成,你不需要自己处理验证。

流程大致是这样的:

域名注册局
   ↓ NS
Cloudflare 权威 DNS
   ↓
Cloudflare 要申请 nekopara.uk 证书
   ↓
Cloudflare 临时放置 TXT DCV 记录
   ↓
CA 查询 DNS
   ↓
验证成功
   ↓
签发 Universal SSL

注意看,CA 验证的实际上是 DNS 记录:
_acme-challenge.nekopara.uk TXT "CA 要求的 token"
而不是去访问:
http://nekopara.uk/.well-known/acme-challenge/...
所以我的全域名 301 重定向:

http://nekopara.uk/*
        ↓ 301
https://www.nekopara.uk/*

根本没有机会干扰这个过程。整个验证发生在 DNS 层,HTTP 层完全不参与。这就是为什么以前“明明也有全域名 301,却从没影响证书”的根本原因。

SaaS Custom Hostname 时代:HTTP DCV

而现在我使用的是 Cloudflare for SaaS 的 Custom Hostname 功能,走的是完全不同的证书系统。架构变成了这样:

nekopara.uk 的 DNS(我使用的 DNS 服务商)
        ↓
通过 CNAME / SaaS Custom Hostname
        ↓
我的 Cloudflare SaaS Zone
        ↓
Cloudflare Edge
        ↓
我的源站

在这个模式下,Cloudflare 不再是自己域名的权威 DNS。它不能简单地通过在自己的 DNS 里放 TXT 记录来证明域名控制权——因为 DNS 根本不在它手里。
Cloudflare 的角色变成了:

“这是某个客户带过来的 Custom Hostname,我需要证明这个具体的 hostname 确实允许挂到我的 SaaS 服务上,并且还要为它单独申请证书。”

Custom Hostname 实际上有两层独立的验证状态:

Custom Hostname ownership validation(证明你拥有这个域名)
          +
SSL certificate DCV(证明你允许 Cloudflare 为它申请证书)

这两者是分开的。更关键的一点是:对于非通配符的 Custom Hostname,只要这个 hostname 已经指向 SaaS target,Cloudflare 会尝试通过 HTTP DCV 来完成证书验证。 即使你最初选择的是 TXT 验证方式,在域名已经指向 SaaS target 后,Cloudflare 仍可能改用 HTTP validation。
于是这次的流程变成了:

Google Trust Services(CA)
        ↓
GET http://nekopara.uk/.well-known/acme-challenge/xxxxx
        ↓
Cloudflare Edge
        ↓
我的 Redirect Rule(nekopara.uk/* → www.nekopara.uk/*)
        ↓
301 Moved Permanently
        ↓
域名所有权验证失败

配置的 301 重定向规则正好进入了 DCV 请求链路,把 CA 的验证请求给重定向走了。

两者的核心差异

场景NS 全托管(以前)Custom Hostname(现在)
DNS 权威Cloudflare另一个 DNS 服务商
证书类型Universal SSLCustom Hostname 独立证书
DCV 方式Cloudflare 自动 DNS TXTHTTP / TXT / Delegated DCV
CA 是否访问 /.well-known/通常不需要HTTP DCV 时必须访问
301 是否影响签发基本不影响(DNS DCV)会影响(HTTP DCV)
是否需要排除 /.well-known/一般没必要很有必要

有趣的是,Cloudflare 的 Rules 文档现在专门提醒了这一点:Redirect Rules 可能影响 Custom Hostname verification、Pages validation 和 HTTP DCV,建议从重定向规则里排除 /.well-known/*

一个容易让人困惑的细节

Universal SSL 并不意味着“不需要 DCV”。它仍然是 DV 证书,CA 当然还是需要确认 Cloudflare 控制这个域名。只是因为 NS 已经交给 Cloudflare 了,所以 Cloudflare 拥有一个非常强的证明手段:

注册局查询:
nekopara.uk NS = alice.ns.cloudflare.com, bob.ns.cloudflare.com
然后 CA 问:
_acme-challenge.nekopara.uk是什么?
Cloudflare 自己的权威 DNS 回答:
TXT = "CA 要求的 token"

整个过程 Web 层完全不用参与。所以可以把 HTTP 层搞得非常暴力:
https://nekopara.uk/* → 301 https://www.nekopara.uk/*
WAF、Worker、源站 Nginx 怎么折腾,都碰不到 DNS DCV。

但 Custom Hostname 的 HTTP DCV 本质上就是:

“既然这个 hostname 已经指到我的 SaaS Edge,那我在这个 hostname 的 /.well-known/ 路径放一个 token,让 CA 直接访问验证。”

这时候 HTTP 层的任何东西都可能把验证搞坏

  • Redirect Rule
  • Worker
  • WAF
  • Access
  • Bot Challenge
  • 源站 301
  • A 记录配错

Cloudflare 的 DCV Troubleshooting 现在也直接把“/.well-known/* 不应该被 redirect”列在检查清单里。

长期建议

所以我现在把规则改成:
(http.host eq "nekopara.uk") and not starts_with(http.request.uri.path, "/.well-known/")
这其实是更正确、更稳健的长期配置。即使现在证书已经签出来了,也不要把这个排除条件删掉——因为证书以后还要续签

把它理解成给 Cloudflare 留一条“基础设施后门”:

正常用户:
nekopara.uk/archives
    ↓ 301
www.nekopara.uk/archives

证书 CA:
nekopara.uk/.well-known/acme-challenge/xxx
    ↓ 不重定向
Cloudflare DCV handler
    ↓ 200 OK + token
域名所有权验证成功

总结

用一句话概括这次踩的坑:

以前 NS 接入时,Cloudflare 掌握权威 DNS,Universal SSL 主要通过 DNS TXT 自动证明域名控制权,所以 HTTP 重定向无关;现在 Custom Hostname 走的是 Cloudflare for SaaS 的独立证书流程,非通配符域名会使用 HTTP DCV,因此我的 301 Rule 正好把 CA 的验证请求也重定向掉了。

这不是“NS 接入的 Cloudflare 比 Custom Hostname 更智能”,而是 DNS DCV 和 HTTP DCV 天生处在不同的网络层。我这次刚好把这个差异完整地踩出来了。

评论区 (0)