使用 HTTPS 回源时,源站可能观察到 回源 Host 与 TLS SNI 不一致。这是本 CDN 产品基于 Cloudflare 自定义主机名提供服务时的正常行为,并不表示请求被篡改或配置未生效。
本文中的域名和 IP 均为示例,请根据实际业务配置替换。
编辑源站时,可以配置以下参数:
HTTP:CDN 节点通过 HTTP 连接源站。HTTPS:CDN 节点通过 TLS 加密连接源站。192.0.2.10。80,HTTPS 使用 443。Host 值,例如 cdn.example.com。需要注意:回源 Host 只控制 HTTP 请求头中的 Host,不会修改 HTTPS 回源时使用的 TLS SNI。
HTTPS 请求分为 TLS 握手和 HTTP 请求两个阶段:
客户端访问 CDN
↓
Cloudflare 与源站建立 TLS 连接
SNI: cdn.example.com.fallback.gnamess.com
↓
TLS 连接建立成功后发送 HTTP 请求
Host: cdn.example.com
两个字段的用途不同:
| 字段 | 所属阶段 | 主要用途 | 示例 |
|---|---|---|---|
| SNI | TLS 握手阶段 | 选择源站 TLS 虚拟主机和证书 | cdn.example.com.fallback.gnamess.com |
| Host | HTTP 请求阶段 | 标识实际请求的业务站点 | cdn.example.com |
本产品对 Cloudflare 的自定义主机名能力进行了统一封装。为了让多个客户域名通过统一且稳定的源站入口建立 TLS 连接,HTTPS 回源使用平台指定的 SNI;控制台中的“回源 Host”则用于设置最终发送给源站的 HTTP Host 请求头。
因此,源站日志中可能看到:
SNI = cdn.example.com.fallback.gnamess.com
Host = cdn.example.com
这是正常的回源结果。
SNI 在 TLS 握手开始时由 Cloudflare 发送,接收连接的源站无法在握手完成后修改它。
在本 CDN 产品中,Cloudflare 账户、回源规则和统一源站域名由平台管理。为保证证书验证、共享回源和平台安全策略正常运行,SNI 由平台统一配置,不作为客户可编辑参数开放。
因此,对于本产品的客户来说:
NGINX 默认不会要求 SNI 与 HTTP Host 必须一致。
处理 HTTPS 请求时,NGINX 通常执行以下流程:
如果 SNI 没有匹配到明确的 server_name,NGINX 通常会使用该地址和端口上的 default_server;如果没有显式配置 default_server,则使用相应监听地址上的第一个 server 配置。因此,SNI 与 Host 不同并不会自动产生错误。
示例配置:
server {
listen 443 ssl default_server;
# 平台使用的统一回源 SNI
server_name cdn.example.com.fallback.gnamess.com;
ssl_certificate /etc/nginx/ssl/cdn.example.com.fallback.gnamess.com.crt;
ssl_certificate_key /etc/nginx/ssl/cdn.example.com.fallback.gnamess.com.key;
location / {
# 业务可以继续使用请求中的原始 Host
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Proto https;
proxy_pass http://127.0.0.1:8080;
}
}
如果需要在 NGINX 日志中同时记录 SNI 和 Host,可以在 http {} 中定义:
log_format sni_host '$remote_addr sni="$ssl_server_name" host="$http_host" '
'request="$request" status=$status';
然后在站点的 server {} 中启用:
access_log /var/log/nginx/sni_host.log sni_host;
Apache 的 mod_ssl 对 SSL 虚拟主机的检查通常比 NGINX 更严格。
TLS 握手时,Apache 根据 SNI 选择 SSL 虚拟主机;TLS 建立后,又根据 HTTP Host 选择处理请求的虚拟主机。如果 SNI 和 Host 分别匹配到不同的 HTTPS 虚拟主机,并且两个虚拟主机的 SSL 配置不兼容,Apache 可能返回:
421 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.
典型过程如下:
TLS SNI: cdn.example.com.fallback.gnamess.com
→ Apache 选择虚拟主机 A
HTTP Host: cdn.example.com
→ Apache 尝试使用虚拟主机 B
虚拟主机 A 与 B 的 SSL 配置不兼容
→ 返回 421 Misdirected Request
常见的不兼容项包括:
mod_ssl 参数不同;如果业务域名数量较少,可以把平台回源域名和业务域名配置到同一个 HTTPS 虚拟主机:
<VirtualHost *:443>
ServerName cdn.example.com.fallback.gnamess.com
ServerAlias cdn.example.com
SSLEngine on
SSLCertificateFile /etc/apache2/ssl/cdn.example.com.fallback.gnamess.com.crt
SSLCertificateKeyFile /etc/apache2/ssl/cdn.example.com.fallback.gnamess.com.key
DocumentRoot /var/www/example
</VirtualHost>
修改后可以运行以下命令,检查域名实际匹配的虚拟主机:
apachectl -S
应确保平台回源 SNI 和业务 Host 不会进入 SSL 配置不兼容的两个 <VirtualHost *:443>。
如果必须保留多个 HTTPS 虚拟主机,应尽量保证相关虚拟主机使用兼容的 SSL 参数,重点检查:
SSLCertificateFile;SSLCertificateKeyFile;SSLProtocol;SSLCipherSuite;SSLVerifyClient;仅修改 SSLStrictSNIVHostCheck 通常不能完整解决该问题,因为 421 可能来自 Apache 对当前 TLS 连接和目标虚拟主机 SSL 配置兼容性的检查。
如果存在大量客户自定义域名,不适合为每个域名维护 Apache ServerAlias,推荐使用 NGINX 统一终止 TLS,再将请求转发给 Apache:
Cloudflare
SNI: 平台统一回源域名
Host: 客户业务域名
↓
NGINX 统一终止 TLS
↓
通过 HTTP 或固定配置的 HTTPS 转发
↓
Apache / 业务应用
推荐优先通过本机 HTTP 转发,避免 Apache 再次执行 SNI 与 Host 的 SSL 虚拟主机检查:
location / {
proxy_pass http://127.0.0.1:8080;
proxy_set_header Host $http_host;
proxy_set_header X-Forwarded-Host $http_host;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header X-Real-IP $remote_addr;
}
如果源站只服务本 CDN 产品,也可以配置一个统一的默认 HTTPS 虚拟主机接收平台回源连接,再由应用根据 HTTP Host 区分业务站点。
使用此方案时,必须同时做好源站访问控制,例如:
apachectl -S,检查 SNI 和 Host 分别匹配到的虚拟主机。在本 CDN 产品中,HTTPS 回源使用平台统一管理的 SNI,而控制台中的“回源 Host”用于设置 HTTP 请求头中的 Host。两者不一致是 Cloudflare 自定义主机名回源的正常设计。
对于客户而言,SNI 属于平台级回源参数,无法在控制台中修改。NGINX 默认允许 SNI 与 Host 不同,因此通常可以正常响应;Apache 可能因两个名称匹配到 SSL 配置不兼容的虚拟主机而返回 421,可通过统一 VirtualHost、统一 SSL 配置或在 Apache 前增加 NGINX TLS 入口解决。