1. 项目背景与问题描述
去年接手公司官网迁移项目时,我们决定将域名托管到Cloudflare以利用其CDN和安全防护能力。本以为HTTPS配置会像官方文档描述的那样"一键开启",实际却遭遇了意料之外的证书冲突问题。当访问者通过浏览器访问时,控制台频繁出现"NET::ERR_CERT_COMMON_NAME_INVALID"错误,导致部分用户无法正常加载页面资源。
这种情况通常发生在混合证书环境中——当服务器原始证书与Cloudflare边缘证书的SAN(Subject Alternative Name)不匹配时,现代浏览器的严格校验机制就会阻断连接。我们当时的配置是:源站使用Let's Encrypt的通配符证书(*.example.com),而Cloudflare默认提供的SSL证书是单域名证书(example.com),这种细微差异正是问题的根源。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. Cloudflare SSL模式选择与验证
2.1 四种SSL模式对比测试
Cloudflare提供四种SSL加密模式,每种模式对证书链的处理方式不同:
| 模式名称 | 源站-Cloudflare加密 | Cloudflare-用户加密 | 需要源站证书 | 典型应用场景 |
|---|---|---|---|---|
| Off | 无 | 无 | 不需要 | 测试环境 |
| Flexible | HTTP | HTTPS | 不需要 | 源站不支持HTTPS |
| Full | HTTPS | HTTPS | 自签名即可 | 源站有证书但不可信 |
| Full (Strict) | HTTPS | HTTPS | 可信CA签发 | 生产环境推荐 |
我们最初选择"Flexible"模式导致浏览器虽然显示绿锁,但实际到源站的连接仍是HTTP,存在中间人攻击风险。切换到"Full"模式后,由于源站证书与Cloudflare证书不匹配,触发了证书错误。
2.2 证书链验证工具
使用OpenSSL命令验证证书链完整性:
bash复制openssl s_client -connect example.com:443 -servername example.com -showcerts | openssl x509 -noout -text
关键检查点:
- 证书的CN(Common Name)和SAN字段是否包含所有使用子域名
- 证书链是否完整(从叶子证书到根证书)
- 证书有效期是否覆盖当前时间
3. 混合证书环境解决方案
3.1 方案一:统一使用Cloudflare源证书
-
在Cloudflare控制台生成Origin CA证书:
- 导航到SSL/TLS > Origin Server > Create Certificate
- 选择有效期(最长15年)
- 添加需要覆盖的所有域名(包括通配符)
-
将生成的证书部署到源站服务器(以Nginx为例):
nginx复制server {
listen 443 ssl;
server_name example.com www.example.com;
ssl_certificate /path/to/origin.crt;
ssl_certificate_key /path/to/origin.key;
# 启用OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
}
注意:Cloudflare源证书不被公共CA信任,仅用于Cloudflare到源站的通信加密
3.2 方案二:保持Let's Encrypt证书并调整校验策略
- 修改证书生成命令,确保包含所有必要域名:
bash复制certbot certonly --nginx -d example.com -d www.example.com -d api.example.com
- 在Cloudflare设置:
- SSL/TLS > Edge Certificates > 启用"Always Use HTTPS"
- 关闭"Authenticated Origin Pulls"(除非专门配置)
- 在Page Rules中添加规则:".example.com/" 设置"SSL=Full (Strict)"
4. 高级配置与性能优化
4.1 启用HTTP/2与0-RTT
在Cloudflare控制台:
- 进入Speed > Optimization
- 启用HTTP/2、HTTP/3及0-RTT连接恢复
- 配置Brotli压缩
对应的Nginx配置示例:
nginx复制http {
# 启用HTTP/2
listen 443 ssl http2;
# 启用OCSP Stapling
ssl_stapling on;
ssl_stapling_verify on;
# 会话恢复设置
ssl_session_timeout 1d;
ssl_session_cache shared:MozSSL:10m;
ssl_session_tickets off;
}
4.2 证书轮换自动化
对于使用Let's Encrypt的方案,建议设置自动续期:
- 创建续期脚本
/etc/letsencrypt/renewal-hooks/post/reload-nginx.sh:
bash复制#!/bin/bash
systemctl reload nginx
- 添加crontab任务:
bash复制0 0 */60 * * certbot renew --quiet --post-hook "/etc/letsencrypt/renewal-hooks/post/reload-nginx.sh"
5. 疑难问题排查指南
5.1 常见错误代码与解决方案
| 错误代码 | 可能原因 | 解决方案 |
|---|---|---|
| ERR_CERT_COMMON_NAME_INVALID | 证书域名不匹配 | 检查SAN列表或改用通配符证书 |
| ERR_SSL_VERSION_OR_CIPHER | 旧版TLS协议被禁用 | 在源站启用TLS 1.2+ |
| ERR_TOO_MANY_REDIRECTS | HTTPS重定向循环 | 检查Page Rules中的重定向规则 |
| ERR_CONNECTION_TIMED_OUT | 源站防火墙拦截Cloudflare IP | 更新源站防火墙白名单 |
5.2 使用cURL进行诊断
bash复制# 检查证书链
curl -vI https://example.com
# 测试不同TLS版本
curl --tlsv1.2 --tls-max 1.2 https://example.com
curl --tlsv1.3 https://example.com
# 检查HSTS头
curl -sI https://example.com | grep -i strict-transport-security
6. 安全加固建议
-
禁用旧协议:
在Cloudflare控制台的SSL/TLS > Edge Certificates中:- 设置最低TLS版本为1.2
- 禁用TLS 1.0和1.1
-
配置严格的CSP头:
在Nginx配置中添加:nginx复制add_header Content-Security-Policy "default-src 'self' cdn.example.com; script-src 'self' 'unsafe-inline'"; -
启用DNSSEC:
在Cloudflare的DNS设置中激活DNSSEC,防止DNS劫持攻击
经过这次踩坑,我总结出Cloudflare HTTPS配置的黄金法则:要么完全信任Cloudflare的证书体系(使用Origin CA),要么确保源站证书与Cloudflare设置严格匹配。混合使用不同来源的证书时,必须仔细检查证书链的每个环节,特别是SAN字段的完整性。
