CloudFront + WebSocket + Caddy 排障实战:从 502 到 TLS SNI 问题定位
在一次将 WebSocket 服务接入 CDN 的过程中,我遇到了一个典型但极具迷惑性的 CloudFront 502 Bad Gateway 问题。本文记录从配置到最终定位 TLS SNI 不匹配 的完整排查过程,希望对类似架构有参考价值。
一、架构说明
整体架构如下:
客户端
↓
CloudFront(CDN)
↓
源站(Caddy + WebSocket 服务)
源站域名(示例):
media.example.com
CloudFront 使用其默认分配域名:
xxxxx.cloudfront.net
目标是通过 CloudFront 访问后端 WebSocket 服务。
二、CloudFront 配置要点
1. Origin 配置
- Origin Domain:
media.example.com - Protocol:HTTPS Only
- HTTPS Port:443
- Minimum TLS:TLSv1.2
2. Cache / Policy
- Cache Policy:
CachingDisabled - Origin Request Policy:
AllViewerExceptHostHeader
3. Viewer 端
- Viewer Protocol Policy:HTTPS Only
- 支持 WebSocket(WSS)
三、初始问题:502 Bad Gateway
访问 CloudFront 域名时返回:
502 Bad Gateway
We can't connect to the server for this app or website at this time.
但源站直接访问完全正常:
curl https://media.example.com
# 返回正常 200
四、第一轮排查:协议与重定向
确认:
- CloudFront Origin 使用 HTTPS ✅
- 源站未返回 HTTP → HTTPS 重定向影响 ❌
结论:不是 308/301 重定向问题。
五、第二轮排查:网络连通性
在源服务器执行抓包:
tcpdump -i any port 443 -n
结果显示:
- CloudFront 节点 IP 可以成功建立 TCP 连接 ✅
- TLS ClientHello 已发送 ✅
- 源站返回 TLS Alert 并立即断开 ❌
关键现象:
ClientHello → Server
Server → TLS Alert → 关闭连接
说明问题发生在 TLS 握手阶段。
六、关键抓包分析(核心突破点)
进一步抓取 TLS 内容:
tcpdump -i any port 443 -n -X
在 ClientHello 中可以看到 SNI 扩展:
Server Name Indication (SNI):
dxxxxxxxxxxxx.cloudfront.net
注意:
- CloudFront 回源时使用的 SNI 是 CloudFront 自身域名
- 而不是你的源站域名
media.example.com
七、根本原因
❗ TLS SNI 不匹配
源站 Caddy 配置监听的是:
media.example.com
但 CloudFront 实际发起 TLS 时:
SNI: xxxxx.cloudfront.net
导致:
- Caddy 无法匹配对应站点
- TLS 握手失败
- 返回 Alert(通常为
internal_error或 handshake failure) - CloudFront 收到异常 → 返回 502
八、为什么 Host 不起作用?
即使:
- HTTP Header 中 Host 是
media.example.com
但在 TLS 层:
SNI 在 TLS 握手阶段决定证书匹配,早于 HTTP 请求
也就是说:
TLS 连接还没建立成功,就已经失败了
九、解决方案
方案一(推荐):配置 fallback SNI
在 Caddy 中允许 fallback:
{
servers {
protocols h1 h2
}
}
media.example.com {
tls {
# 确保支持 SNI fallback
}
reverse_proxy 127.0.0.1:10000
}
或使用 Caddy 默认自动证书机制,确保能匹配任意 SNI。
方案二:让 CloudFront 使用正确 SNI(不可控)
CloudFront 无法修改 SNI,这是其设计限制:
回源 SNI = Origin Domain / CloudFront 内部域名
因此不能从 CloudFront 侧修正。
方案三:使用泛域名证书
确保源站证书满足:
- 支持
media.example.com - 或 wildcard:
*.example.com
并确保 Caddy 可以匹配请求域名。
方案四:关闭严格 SNI 绑定(关键)
在 Caddy / Nginx 中避免强依赖 SNI 精确匹配:
- 使用默认站点 fallback
- 或配置 catch-all server block
十、最终验证
修复后再次访问:
https://xxxxx.cloudfront.net
结果:
- TLS 握手成功 ✅
- CloudFront 返回 200 ✅
- WebSocket 正常升级 ✅
十一、排查经验总结
1. 502 不一定是网络问题
CloudFront 502 常见原因:
- TLS 握手失败
- SNI 不匹配
- 证书不兼容
- 源站拒绝连接
2. 抓包是关键手段
推荐命令:
tcpdump -i any port 443 -n -X
重点观察:
- ClientHello 中的 SNI
- Server 返回的 TLS Alert
- 是否完成握手
3. 排查路径建议
- curl 直连源站
- tcpdump 看 TCP 是否通
- 分析 TLS ClientHello
- 检查证书与 SNI
- 查看服务器日志
- 排除 CloudFront 配置
十二、结论
本次问题的本质是:
CloudFront 回源使用的 SNI 与源站证书/服务域名不匹配,导致 TLS 握手失败,从而引发 502 Bad Gateway。
这一类问题具有典型特征:
- TCP 正常
- TLS 失败
- 服务器日志可能无记录
- 502 快速返回
如果你在类似架构中遇到 CloudFront / Cloudflare / Caddy / WebSocket 组合问题,建议优先检查:
SNI + TLS 握手 + 证书匹配
这是最容易被忽略但最关键的一环。
如果你需要,我可以再帮你把这篇文章再升级成「生产级排障 checklist + 标准 SOP」,用于你后续复用。