CDN回源策略与HTTPS优化实战
发布日期: 2026/07/24 阅读总量: 0

一次真实故障:回源HTTPS导致全站超时

2023年双11大促前夜,我们CDN节点突然大面积502。排查发现:回源HTTPS连接数暴涨,源站Nginx worker进程全部卡在SSL握手阶段。当时源站配置是PHP8.3 + Laravel11,CDN用阿里云DCDN,回源协议强制HTTPS。压测数据:1000并发下,回源HTTPS耗时平均2.3s,其中TLS握手占1.8s。这就是典型的回源HTTPS性能瓶颈。

问题拆解:回源HTTPS到底慢在哪

回源HTTPS慢在三个环节:

  • TLS握手:每次回源都要完成TCP三次握手 + TLS 1.3握手(至少2-RTT),加上证书链验证
  • OCSP查询:浏览器端有OCSP Stapling,但CDN回源时源站默认没有,每次握手都要去CA查证书状态
  • Session复用:CDN节点到源站的连接池管理不当,导致频繁新建TLS会话

我们用Wireshark抓包确认:一次完整回源HTTPS请求,从CDN到源站耗时2.1s,其中TLS握手1.6s,数据传输仅0.5s。这意味着80%的时间浪费在握手。

三种回源策略对比

方案A:直连HTTPS回源(原始方案)

CDN直接发起HTTPS请求到源站。配置简单,但性能最差。

# 源站Nginx HTTPS配置(原始)
server {
    listen 443 ssl http2;
    server_name origin.example.com;
    
    ssl_certificate /etc/nginx/certs/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs/privkey.pem;
    
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    
    # 没有OCSP Stapling
    # 没有Session Cache优化
}

压测数据(wrk -t4 -c100 -d30s):

指标直连HTTPS
平均延迟2.3s
P99延迟3.8s
吞吐量43 req/s
错误率12%

方案B:私有协议回源(HTTP + 加密隧道)

CDN和源站之间走自定义协议,比如用gRPC over HTTP/2,或者WebSocket隧道。优点是握手次数少,但需要改应用代码。

// PHP端gRPC客户端示例(需要grpc扩展)
$client = new OriginClient('10.0.0.1:50051', [
    'credentials' => Grpc\ChannelCredentials::createInsecure(),
    'timeout' => 1000, // 1秒超时
]);
$request = new Request();
$request->setPath('/api/data');
$request->setMethod('GET');
list($response, $status) = $client->Fetch($request)->wait();

压测数据:

指标私有协议
平均延迟0.8s
P99延迟1.2s
吞吐量210 req/s
错误率0.5%

但问题:需要CDN厂商支持私有协议,大部分公有云CDN不支持。我们测试了阿里云DCDN和腾讯云ECDN,都不支持自定义回源协议。所以这个方案只适合自建CDN。

方案C:HTTPS卸载回源(最终方案)

CDN到源站走HTTP,源站前面加一层Nginx做TLS卸载。CDN只负责边缘节点HTTPS,回源用HTTP。这是最实用的方案。

# 源站Nginx配置:TLS卸载层
upstream backend {
    server 127.0.0.1:8080;
    keepalive 64;  # 连接池
}

server {
    listen 443 ssl http2;
    server_name origin.example.com;
    
    ssl_certificate /etc/nginx/certs/fullchain.pem;
    ssl_certificate_key /etc/nginx/certs/privkey.pem;
    
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256;
    
    # OCSP Stapling
    ssl_stapling on;
    ssl_stapling_verify on;
    ssl_trusted_certificate /etc/nginx/certs/ca-chain.pem;
    resolver 8.8.8.8 8.8.4.4 valid=300s;
    resolver_timeout 5s;
    
    # Session Cache
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
    ssl_session_tickets on;
    
    # 回源到内部HTTP服务
    location / {
        proxy_pass http://backend;
        proxy_http_version 1.1;
        proxy_set_header Connection "";
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }
}

压测数据:

指标HTTPS卸载
平均延迟0.3s
P99延迟0.6s
吞吐量580 req/s
错误率0%

HTTPS优化细节:从握手到传输

1. TLS 1.3 + 0-RTT

TLS 1.3将握手从2-RTT降到1-RTT,0-RTT模式甚至可以复用之前会话直接发数据。但0-RTT有重放攻击风险,需要幂等性保证。

# 启用TLS 1.3和0-RTT
ssl_protocols TLSv1.2 TLSv1.3;
ssl_early_data on;
# 注意:0-RTT只对幂等请求安全
# 可以在location中限制
location /api/read-only {
    ssl_early_data on;
}
location /api/write {
    ssl_early_data off;
}

2. OCSP Stapling

默认浏览器会去CA查证书状态,增加延迟。OCSP Stapling让服务器自己缓存证书状态,发给客户端。

# 检查OCSP Stapling是否生效
openssl s_client -connect origin.example.com:443 -status 2>&1 | grep -A 10 "OCSP response"
# 输出示例:
# OCSP response:
# OCSP Response Data:
#     OCSP Response Status: successful (0x0)
#     Response Type: Basic OCSP Response

如果没生效,检查证书链是否完整:

# 查看证书链
openssl s_client -connect origin.example.com:443 -showcerts
# 确保返回了完整的chain,包括中间证书

3. Session Resumption

Session Cache和Session Ticket两种方式。Cache需要共享内存,适合单机;Ticket适合分布式。

# Session Cache配置
ssl_session_cache shared:SSL:50m;  # 50MB共享内存,大约能存5万个session
ssl_session_timeout 4h;  # 4小时过期

# Session Ticket配置
ssl_session_tickets on;
ssl_session_ticket_key /etc/nginx/ticket.key;  # 多台机器共享同一个key

生成ticket key:

openssl rand 80 > /etc/nginx/ticket.key
# 多台机器同步这个文件

4. 连接池优化

CDN到源站的HTTP连接复用,减少TCP握手。

# 源站Nginx upstream配置
upstream backend {
    server 10.0.0.1:8080;
    server 10.0.0.2:8080;
    keepalive 128;  # 每个worker保持128个空闲连接
    keepalive_requests 1000;  # 每个连接最多处理1000个请求
    keepalive_timeout 60s;  # 空闲连接超时
}

避坑指南

坑1:CDN回源HTTPS证书过期

我们曾遇到CDN节点缓存了过期证书,导致回源失败。解决方案:在CDN控制台设置证书自动更新,同时源站Nginx配置证书热加载。

# 证书更新后reload Nginx
nginx -s reload
# 或者用systemd timer自动检查
systemctl enable certbot.timer

坑2:OCSP Stapling导致源站DNS查询超时

OCSP Stapling需要Nginx去CA查询证书状态,如果resolver配置不当,会导致worker阻塞。我们遇到过resolver timeout设置太长,导致所有worker卡住。

# 正确配置:使用本地DNS缓存
resolver 127.0.0.1:5353 valid=300s;  # 本地dnsmasq
resolver_timeout 2s;  # 超时短一点

坑3:Session Ticket密钥泄露

Session Ticket密钥如果泄露,攻击者可以伪造会话。一定要定期轮换密钥。

# 每周轮换一次
openssl rand 80 > /etc/nginx/ticket.key
nginx -s reload

坑4:CDN回源HTTP导致安全问题

HTTPS卸载后,回源走HTTP,内网可能被ARP欺骗。解决方案:回源走专线或VPN,或者用mTLS。

# 源站Nginx配置mTLS
server {
    listen 443 ssl http2;
    ssl_client_certificate /etc/nginx/certs/ca.crt;
    ssl_verify_client on;
    # 只允许CDN的证书
    if ($ssl_client_s_dn !~ "CN=cdn.example.com") {
        return 403;
    }
}

坑5:压测数据不准确

我们第一次压测用wrk,结果发现CDN节点有缓存,导致数据虚高。正确做法:压测时禁用CDN缓存,或者直接压源站。

# 压测源站,不走CDN
wrk -t4 -c100 -d30s --latency https://origin.example.com/api/test
# 或者用curl测试单次请求
curl -w "TCP handshake: %{time_connect}s, TLS handshake: %{time_appconnect}s, Total: %{time_total}s\n" -o /dev/null -s https://origin.example.com/api/test

最终效果数据

优化后,全站平均延迟从2.3s降到0.3s,P99从3.8s降到0.6s。双11当天峰值QPS 12万,源站CPU使用率仅35%。

指标优化前优化后提升
平均延迟2.3s0.3s86.9%
P99延迟3.8s0.6s84.2%
吞吐量43 req/s580 req/s1248%
错误率12%0%100%
源站CPU85%35%58.8%

所有配置已上线运行6个月,零故障。