一个容易被忽略的问题:服务器看到的端口不是用户的端口——CDN Source
配置前的检查
如果你正在处理CDN Source,先别急着照搬网上的参数。假设你的网站使用了CDN加速,并且CDN又回源到了另一层反向代理(比如Nginx),最终才到达你的源服务器。此时你可能会发现,源服务器上记录的所有连接都来自同一个IP和同一个端口,比如 203.0.113.5:12345。但真实用户明明分布在全国各地,他们的源端口应该各不相同才对。这个现象就是多层CDN代理叠加场景下的源端口丢失。
对大多数网站来说,丢失源端口似乎不影响访问——网页照样能打开。但如果你需要基于源端口做日志审计、DDoS溯源、或某些安全策略(比如源端口白名单),端口丢失就会带来大麻烦。更糟糕的是,你甚至可能误把CDN节点的IP当成攻击者而误封。
下面我们一步步拆解这个问题,即使你只有网络基础知识也能看懂。
什么是源端口?为什么它在多层代理中会丢失?与CDN Source
源端口是什么
每个TCP连接都由四元组唯一标识:源IP、源端口、目标IP、目标端口。当你访问百度时,你的浏览器会随机分配一个临时端口(比如 56789)和百度的80端口建立连接。这个临时端口就是源端口。服务器可以通过源端口区分来自同一客户端的多个并发连接。
普通代理场景:第一次丢失
当你只使用一层CDN时,CDN节点会替你转发请求。标准做法是:
- CDN节点作为客户端去连接你的源服务器
- 此时源服务器看到的是CDN节点的IP和端口,而不是你的真实源IP和端口
为了解决源IP丢失问题,大家用 X-Forwarded-For 头传递真实IP。但源端口没有类似的标准化机制。HTTP 头 X-Forwarded-For 只记录IP,不记录端口。所以第一层丢失的是源端口。
多层CDN代理:雪上加霜
如果你的架构中有两层CDN(比如Cloudflare → 又拍云 → 自建Nginx → 源站),那么:
- 用户请求到Cloudflare,Cloudflare向上游又拍云发起新的TCP连接,源端口变为Cloudflare节点的某个端口
- 又拍云收到后,再向你的Nginx发起连接,源端口变为又拍云节点的端口
- Nginx再转发到源站,源站看到的源端口是Nginx的连接端口
最终源服务器无法知道真实用户到底用了哪个源端口。而且不同代理层还可能复用端口,导致日志中大量“重复端口”,让溯源完全失效。
延伸阅读:此处可内链到“CDN Source配置案例”相关文章。
补充参考:此处可内链到“CDN Source故障排查实例”。
为什么源端口很重要?三个典型场景
1. 安全日志和攻击溯源
当你遭受DDoS或扫描攻击时,源端口可以帮助你区分不同攻击源。如果所有请求看起来都来自同一个端口(比如443),你无法判断是多个用户还是单个攻击者在不断重连。
2. 基于源端口的访问控制
有些企业内网应用使用端口白名单——只允许特定源端口段的连接。一旦经过CDN,所有请求的源端口都变了,白名单策略失效。
3. 连接数与会话管理
一些统计系统依赖源端口计算独立连接数。端口丢失后,统计结果会严重失真。
解决思路:从协议层面恢复源端口
方案一:使用 PROXY protocol
PROXY protocol 是一个在TCP连接开头附加的协议,专门用于在代理之间传递客户端的原始IP、端口等信息。它由HAProxy作者设计,广泛被Nginx、HAProxy、AWS ELB等支持。
- 工作原理:在TCP连接建立后,代理首先发送一个文本头部,格式如:
PROXY TCP4 192.0.2.1 203.0.113.5 56789 80,其中第4个字段是真实源端口(56789),第5个是目标端口(80)。 - 要求:所有代理层都必须支持并启用PROXY protocol,并且只能在TCP层传输,不能用于HTTP/2或加密连接。
- 配置示例(Nginx):在代理的listen指令后加上
proxy_protocol,并在后端配置中开启set_real_ip_from和real_ip_header proxy_protocol。
PROXY protocol 是目前解决多层代理源端口问题的最可靠方案,它完整保留了源IP+源端口。
方案二:扩展HTTP头部传递端口
由于PROXY protocol 需要底层支持,有些CDN厂商提供了自定义HTTP头传递源端口。例如:
X-Forwarded-PortX-Real-Port- 或者合并到
X-Forwarded-For中,格式为client-ip:port(非标准,但部分负载均衡器支持)
这种方式的缺陷是:
- 多层代理可能覆盖或丢失头部值
- 需要源服务器解析自定义头部,且无法保证所有代理都正确附加
方案三:利用四层透明代理(TProxy)
如果你完全控制网络层,可以在源站上使用 TProxy 技术,让代理服务器通过iptables规则拦截数据包并保持原始源IP和端口。但这需要非常底层的网络配置,且大部分CDN节点不支持。
进阶阅读:此处可内链到“CDN Source性能优化”指南。
实际部署建议:针对多层CDN场景的步骤
第一步:确认每一层是否支持PROXY protocol
检查你的CDN供应商文档:Cloudflare 支持(需要开启“Proxy Protocol”选项)、又拍云支持(需配置)、AWS CloudFront 支持(通过“Origin Protocol Policy”中的“PROXY protocol”)等。自建Nginx和HAProxy天然支持。
第二步:从最顶层CDN开始启用
在最外层的CDN(离用户最近的一层)开启PROXY protocol发送,然后在内层CDN或Nginx的 listen 指令加上 proxy_protocol 参数,同时配置 proxy_protocol 向后端传递。确保链条不断。
第三步:在源服务器上正确解析
如果源站是Nginx,需要在server块中添加:
listen 80 proxy_protocol;
set_real_ip_from 0.0.0.0/0;
real_ip_header proxy_protocol;
real_ip_recursive on;
之后,$remote_addr 和 $remote_port 就会变成真实的客户端IP和端口。
第四步:验证
在源站访问日志中添加 $remote_port,然后使用curl从不同端口访问,检查日志是否记录了你客户端的随机端口。
常见误区与陷阱
误区1:X-Forwarded-For 也包含端口?
标准RFC 7239定义的X-Forwarded-For和Forwarded头部支持端口,但实际实现中很少使用。例如Forwarded: for=192.0.2.1:56789 是合法的,但很多CDN不填充端口部分。
陷阱1:PROXY protocol 会破坏非代理连接
如果客户端直接连接启用了proxy_protocol的端口,客户端发送的第一个字节会被解析成PROXY协议头,导致连接失败。所以需要确保只有代理层的连接走这个端口,或者单独开一个端口。
陷阱2:多层CDN中的时序问题
某些CDN为了性能,会在内部复用连接池,导致PROXY protocol头中的源端口不是用户端口而是CDN节点内部端口。需要确认CDN厂商是否真正透传原始信息。
总结
容易忽略的细节
源端口丢失是多层CDN架构中容易被忽视的问题。随着Web应用对安全审计和精确溯源的要求越来越高,恢复真实源端口变得必要。目前最成熟的解决方案是统一使用PROXY protocol,从用户端到源站每一层都开启支持。虽然配置有一定复杂度,但一旦配好,就如同给请求打上了“指纹”,让你能精准定位每一个连接。
如果你正在使用多层CDN,不妨检查一下日志中的remote_port,看看是否已经丢失。如果丢失,现在动手改造还不晚。把这些步骤跑通后,CDN Source基本就能稳定落地。
延伸阅读
