前言

之前由于 Cloudflare CDN Partner 告知关闭注册通道后,虽然我在某个第三方的 Partner 平台早已注册好了账号,那会还是能够继续使用的,但是后续的 CF 边缘证书不再继续更新,这变相的也没办法用了,我也便早早迁到了 SaaS

主服务器配置好所有站点都可以正常使用,DNS CNAME 解析全部正常,也能够使用优选

然后由于主服务器配置不够跑 Docker,我便在另外的服务器上运行项目。由于是不同的服务器,DNS 解析的默认回源地址无法使用,我便新加了一个自定义回源地址

配置好证书和服务器配置文件后运行没有问题,然后一旦我 SSL 切换成完整(严格)模式,网站立马报 526 错误

Error Code 526

不过切换成完整模式能正常访问,本着能跑就不动的真理,实在没有时间精力研究那会便就放着不管

然而最近兴趣旺盛想搞新项目,发现本来一个站点能访问,在我新加了一个站点配置文件后出现了 421

Cloudflare SaaS 自定义回源多服务器 Full Strict SSL 报 526 / 421 Misdirected Request 原因与解决方法

试了下切换完整(严格)依然 526,便顺便都把问题解决掉了

问题背景

我在 CF SaaS 中已经自定义好了一个回源地址,默认只能添加一个回退源

主站 tdeh.top 及相关所有二级站点(在同一服务器上)使用 CF 解析加速全部正常,SSL 切换完整(严格)模式也正常,现在这些均通过我的默认回退源 cf.tdeh.com(举例),证书服务器也配置好都能够校检,均能访问

但是当我把 1.tdeh.top 放到另一台服务器,并设置一个新的自定义回源地址为 cc.tdeh.com 后,SSL 切换完整(严格)后先是出现

  • Error 526
  • Invalid SSL certificate

切换回完整(无证书验证后)访问正常,然后在我把 2.tdeh.top 也放上去后出现

  • Misdirected Request
  • The client needs a new connection for this request as the requested host name does not match the Server Name Indication (SNI) in use for this connection

开始以为我证书出了问题,检查是受信任的机构自签没问题后,然后服务器配置文件排查了半天依然无解,刚好出现的 421 错误给我指明了方向

所以最终理清的现状是

  • 主服务器 tdeh.top 正常访问
    • 默认回源地址:cf.tdeh.com
    • SNI:tdeh.top
    • 证书:*.tdeh.top
  • 另一台服务器新增 1.tdeh.top + 2.tdeh.top
    • 新自定义回源地址:cc.tdeh.com
    • SNI:cc.tdeh.com
    • 虚拟主机配置 + 证书导致 526 / 421

原因分析

这个问题的核心是:

  • CF SaaS 自定义回源后,回源 TLS 握手使用的是 cc.tdeh.com 作为 SNI
  • 但 HTTP 请求 Host 仍然可能是 1.tdeh.top / 2.tdeh.top
  • 服务器 Apache 把 1.tdeh.top、2.tdeh.top 分成了多个独立 SSL VirtualHost
  • 就会出现 SNI 和 Host 不在同一个 vhost 的问题

也就是 CF 的回源链路变成了

访问:1.tdeh.top
↓
Cloudflare
↓
自定义回源:cc.tdeh.com
↓
TLS SNI:cc.tdeh.com
↓
HTTP Host:1.tdeh.top
↓
Apache

CF 的新增自定义回源 SNI 默认是 Custom Origin Name,Apache 会先根据 TLS 握手里的 SNI 命中 cc.tdeh.com 这个虚拟主机

但请求头里的 Host 又是 1.tdeh.top。如果 1.tdeh.top 在另一个 <VirtualHost *:443> 里,Apache 就会认为:

  • TLS 连接用的是 cc.tdeh.com
  • HTTP 请求却要访问 1.tdeh.top
  • 两者不属于同一个 SSL vhost
    SNI 命中和 Host 不同

理论上普通多站点可独立,但 SaaS 自定义回源要求 统一 vhost 才能匹配 SNI

在 Full Strict 或 Full Strict(严格)下

  • TLS session 已绑定 SNI
  • 多域名复用同一连接,Apache 会报 Misdirected Request

然后就会出现 526 / 421

主站为什么没问题

主站虽然回源地址是:cf.tdeh.com

但是 CF 回源时实际校验的 TLS/SNI 仍然能和源站返回的 *.tdeh.top 证书匹配,所以 SSL 可以通过

而 SaaS 自定义回源到另一台服务器后,Cloudflare 统一使用 cc.tdeh.com 作为回源 TLS 校验入口

所以并不是访问 1.tdeh.top 就一定校验证书 1.tdeh.top

在自定义回源里 CF 先校验自定义回源入口的 HTTPS,然后再把 Host 的请求转发进去

默认回退源和自定义回源在实际应用中走的路线不一样,自定义回源是 Custom Origin Name,服务器不设置在同一个配置文件里的话,CF 会复用连接/单个 SNI 校验多域名

解决方法

问题找到了吗,解决就简单了其实

我本来并没有设置 cc.tdeh.com 为一个独立的站点 ,也没有配置证书文件,因为并没有想到自定义回源跟默认回退源居然是有差别的

在服务器配置文件中,不要再拆分多个 vhost,把你的自定义回源地址 cc.tdeh.com 作为统一的 HTTPS 回源入口,然后把本服务器所有域名写进同一个 ServerAlias

服务器不管是 Apache 还是 Nginx 原理都是一样的,Nginx 需要把自定义回源的 TLS 入口作为 统一 server block

Apache 示例

<VirtualHost *:443>
    # 统一回源入口域名
    ServerName cc.tdeh.com
    # 所有需要访问的域名
    ServerAlias 1.tdeh.top 2.tdeh.top

    SSLEngine on
    SSLCertificateFile /etc/apache2/cc.tdeh.com.crt
    SSLCertificateKeyFile /etc/apache2/cc.tdeh.com.key

    # 禁止访问默认站点 cc.tdeh.com
    DocumentRoot /var/www/empty
    <Directory /var/www/empty>  
        Require all denied  
    </Directory>

    RewriteEngine On

    # 1.tdeh.top → /var/www/1
    RewriteCond %{HTTP_HOST} ^1\.tdeh\.top$ [NC]
    RewriteRule ^/(.*)$ /var/www/1/$1 [END]

    # 2.tdeh.top → /var/www/2
    RewriteCond %{HTTP_HOST} ^2\.tdeh\.top$ [NC]
    RewriteRule ^/(.*)$ /var/www/2/$1 [END]

    <Directory /var/www/1>
        Options FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    <Directory /var/www/2>
        Options FollowSymLinks
        AllowOverride All
        Require all granted
    </Directory>

    # 当然想用反代也是可以的
    ProxyRequests Off
    ProxyPreserveHost On
    RewriteEngine On
    RewriteCond %{HTTP_HOST} ^1\.tdeh\.top$ [NC]
    RewriteRule ^/(.*)$ http://127.0.0.1:3000/$1 [P,L]
</VirtualHost>

重点其实是需要放在同一个 <VirtualHost *:443> 里,相当于就是同一个配置文件了,因为 CF 的回源入口是 cc.tdeh.com,所以 Apache 必须让它同时接受所有的域名

关于证书

在这种配置下,只需要为自定义回源地址的域名配置证书就好

因为 CF 回源校验的是 cc.tdeh.com,源站只需要这张证书就能校检,新增的域名证书已经在 SaaS 中添加主机名的时候已经颁发完毕了(应该是相当于之前的边缘证书)

结语

这个问题无关 DNS,证书,服务器多虚拟主机配置文件的问题,而是 CF SaaS 的自定义回源到另一台服务器后,TLS SNI 使用的是自定义回源域名,而 HTTP Host 仍然是新增的需要访问的域名

如果 Apache 把这些域名拆到不同 SSL VirtualHost,就会出现 SNI 与 Host 不匹配,最终导致 421 Misdirected Request 或 CF 526

本质是默认回退源与 SaaS 自定义回源的回源 SNI 逻辑不同


活着就是为了改变世界