
网站接入 CDN 后,Nginx、Apache 或应用日志里的访客 IP 全变成 CDN 节点地址,通常属于正常现象。源站实际建立 TCP 连接的对象就是 CDN 节点,所以服务器默认记录节点 IP;真实访客地址需要由 CDN 放进请求头,再由源站在可信范围内还原。
这件事不能简单处理成“读取 X-Forwarded-For 的第一个值”。任何客户端都可以自行发送同名请求头。如果源站无条件信任它,攻击者就能伪造 IP,影响登录审计、评论记录、限流、黑名单和安全插件判断。
正确做法是:先确认 CDN 实际传递哪个头,只信任官方公布的 CDN 或反向代理地址,再由 Web 服务器恢复客户端 IP,同时保留原始连接地址和完整代理链用于审计。
为什么源站只能直接看到 CDN 节点 IP
没有 CDN 时,访客通常直接与源站建立连接,Web 服务器能从连接层拿到客户端地址。接入 CDN 后,请求路径变成:
访客 -> CDN 边缘节点 -> 可能存在的负载均衡或反向代理 -> 源站
对源站而言,连接它的是最后一层 CDN 节点或代理设备,因此 $remote_addr、REMOTE_ADDR 等默认字段显示节点 IP 是正常结果。
CDN 知道最初的访问者是谁,通常会通过以下请求头把信息带到源站:
X-Forwarded-For:常见的代理链头,可能包含多个以逗号分隔的地址。X-Real-IP:部分代理使用的单一客户端地址头。Forwarded:RFC 7239 定义的标准化代理信息头,但实际使用范围和格式要看链路组件。- 厂商专用头:例如 Cloudflare 的
CF-Connecting-IP。其他 CDN 可能使用不同名称。
不要只凭网上教程猜请求头名称。先查看当前 CDN 控制台的回源设置和官方文档,再在源站临时记录相关头,确认请求实际带来了什么。
X-Forwarded-For 里有多个 IP,哪个才是真实访客
典型的 X-Forwarded-For 链可能类似:
198.51.100.23, 192.0.2.41, 192.0.2.58
常见约定是左侧更接近原始客户端,右侧更接近当前服务器。但“最左边就是访客 IP”只在整条代理链都正确清洗和追加请求头时才成立。
假设客户端主动发送:
X-Forwarded-For: 203.0.113.99
如果 CDN 或第一层代理没有覆盖不可信内容,只在原值后面追加地址,源站看到的最左侧值就可能是伪造的。更可靠的判断方式是从连接源开始,自右向左移除已知可信代理,遇到第一个不可信地址时将其视为客户端候选。
Nginx real_ip_recursive on、Apache mod_remoteip 和应用框架的“trust proxy”配置,都依赖一份明确的可信代理范围。缺少这项边界时,程序只能盲目相信请求头里的一串文本。
修改配置前先确认三件事
1. 源站前面到底有几层代理
除了 CDN,还可能有云负载均衡、WAF、宝塔或 1Panel 自带的反向代理、Ingress、容器网关和另一层 Nginx。真实链路比控制台里看到的域名解析更重要。
画出实际顺序,并确认最后是谁连接源站。如果 CDN 后面还有负载均衡,源站需要同时正确处理负载均衡和 CDN 之间的地址链。
2. CDN 使用哪个请求头
检查 CDN 官方文档和回源请求头配置。有些平台默认传递 X-Forwarded-For,有些提供专用真实 IP 头,也可能允许自定义回源头。
如果使用厂商专用头,配置更直观,但要避免把 A 厂商的头套到 B 厂商上。以后更换 CDN 时,这一项也要一起迁移。
3. CDN 节点 IP 段从哪里获取
可信代理范围必须来自 CDN 官方公布的最新 IP 段,不能写成 0.0.0.0/0,也不要从访问日志里随便整理几个地址当白名单。
节点地址可能更新。生产环境应记录来源和更新时间,定期同步,并在修改前检查配置、保留旧文件和准备回滚。
Nginx 恢复真实访客 IP
Nginx 使用 ngx_http_realip_module 改写客户端地址。先检查当前构建是否包含该模块:
nginx -V 2>&1 | grep -- --with-http_realip_module
下面是使用 X-Forwarded-For 的通用结构,示例网段必须替换为 CDN 官方提供的真实网段:
http {
# 只信任 CDN 或前置代理的官方地址段
set_real_ip_from 192.0.2.0/24;
set_real_ip_from 2001:db8:1234::/48;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
log_format main_realip
'$remote_addr peer=$realip_remote_addr '
'xff="$http_x_forwarded_for" '
'"$request" $status $body_bytes_sent';
access_log /var/log/nginx/access.log main_realip;
}
这几个字段的作用不同:
$remote_addr:realip 模块处理后,供访问控制和日志使用的客户端地址。$realip_remote_addr:原始连接对端地址,可用于确认请求是否真的来自 CDN 节点。$http_x_forwarded_for:请求携带的完整 XFF 链,便于排查多级代理。
如果 CDN 使用专用头,应按官方说明替换 real_ip_header。例如使用单值真实 IP 头时,是否需要 real_ip_recursive 要根据该头的语义决定。
修改后先检查语法,再平滑加载:
sudo nginx -t
sudo systemctl reload nginx
不要在 nginx -t 失败时继续重启服务。保留修改前的配置文件,出现异常时可以迅速恢复。
Nginx 作为中间反向代理时怎么传递 XFF
如果前端 Nginx 还要把请求转发给应用服务器,通常使用:
location / {
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;
proxy_pass http://app_backend;
}
$proxy_add_x_forwarded_for 会在现有 XFF 后追加当前 $remote_addr。前提是上一层的真实 IP 恢复和信任边界已经正确,否则错误或伪造的地址会继续传到应用。
应用服务器应只接受来自这台 Nginx 的流量,并把它配置为可信代理。若应用端口直接暴露公网,攻击者可能绕过 CDN 和 Nginx,直接提交伪造头。
Apache 使用 mod_remoteip
Apache 2.4 可以通过 mod_remoteip 恢复客户端地址。下面同样只展示配置结构:
RemoteIPHeader X-Forwarded-For
RemoteIPTrustedProxy 192.0.2.0/24
RemoteIPTrustedProxy 2001:db8:1234::/48
LogFormat "%a peer=%{c}a xff=\"%{X-Forwarded-For}i\" %r %>s %b" realip
CustomLog logs/access_log realip
%a 表示模块处理后的客户端地址,%{c}a 保留底层连接地址。修改后先运行配置检查:
sudo apachectl configtest
确认返回 Syntax OK 后再平滑重载。不同发行版的模块启用方式和配置目录不同,应以当前系统为准。
应用框架不要自己随便拆 XFF
如果 Nginx 或 Apache 已经安全恢复了客户端地址,PHP、WordPress 和大多数后端程序优先使用 Web 服务器提供的远端地址,不必在每个插件里重复解析 XFF。
直接使用下面这种逻辑风险很高:
$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];
它既没有验证连接来源,也没有处理多个地址,更无法判断头是否由客户端伪造。
Express 等框架提供 trust proxy 配置。应按实际代理层数或明确网段设置,不要因为“经过 CDN”就直接设置为无条件信任:
app.set('trust proxy', [
'loopback',
'192.0.2.0/24',
'2001:db8:1234::/48',
]);
如果同一应用存在长短不同的访问路径,仅按固定跳数信任代理可能被绕过。按明确的代理地址范围判断通常更稳妥。
WordPress 需要注意什么
WordPress 评论、登录审计、限流和安全插件经常依赖 REMOTE_ADDR。如果服务器层没有恢复真实 IP,这些功能会把大量访客识别成同一个 CDN 节点,可能出现以下问题:
- 评论记录和登录日志显示同一批节点地址。
- 按 IP 限流时,一个节点上的访客相互影响。
- 封禁某个节点 IP 后,一批正常用户同时无法访问。
- 安全插件误判暴力破解来源,或者无法定位真实攻击地址。
优先在 Nginx 或 Apache 层修复,这样 WordPress、PHP-FPM 和其他站点可以使用一致的客户端地址。只有插件明确要求专用配置时,再按插件文档处理。
配置完成后如何验证
不要只看一条新日志就认定配置成功。至少做下面几组检查。
同时记录恢复后的 IP、连接地址和完整 XFF
从手机网络、家庭宽带和其他外部网络分别访问测试页面,确认:
$remote_addr或%a随真实访问网络变化。- 原始连接地址仍属于 CDN 官方节点范围。
- XFF 链条顺序符合当前代理结构。
- IPv4 和 IPv6 访问都能正常识别。
测试伪造请求头是否会被信任
在不经过可信代理的测试路径中,自行添加一个 XFF 值。若源站把这个值直接当成访客 IP,说明信任范围过宽。
生产源站最好通过防火墙或安全组只允许 CDN、负载均衡和管理网络访问。这样即使应用配置出现疏漏,攻击者也较难绕过 CDN 直接连接源站。
检查业务功能
验证登录日志、评论 IP、WAF 或安全插件、接口限流、风控策略和后台统计。恢复真实 IP 会改变这些功能看到的地址,原有节点 IP 黑名单和限流规则可能需要清理。
常见错误及修复方向
把所有来源都设为可信代理
set_real_ip_from 0.0.0.0/0 或信任全部 IPv6 地址,相当于允许任何客户端声明自己的 IP。应改为 CDN 和内部代理的明确地址段。
只取 XFF 最左侧值
多级代理环境下,最左侧可能是客户端预先伪造的内容。应从可信连接源开始,根据代理链和可信范围向左判断。
只改日志格式,没有恢复地址
直接把 $http_x_forwarded_for 写进日志能看到请求头,但 Nginx 访问控制、PHP 的 REMOTE_ADDR 和应用限流仍可能使用节点 IP。要根据业务决定是否启用 realip 模块,单独替换日志字段解决不了这些问题。
配置一次后不再更新节点网段
CDN 节点地址可能变化。网段缺失会导致部分地区再次记录节点 IP;保留了失效范围,则可能扩大不必要的信任边界。应建立定期更新、语法检查和回滚流程。
源站仍允许公网直连
攻击者可以绕开 CDN 访问源站,既能暴露源站地址,也可能利用被错误信任的头伪造客户端 IP。完成真实 IP 配置后,还要收紧安全组、防火墙和源站域名访问策略。
网站接入 CDN 后看到节点 IP,是代理链路的自然结果。恢复真实访客地址需要先划清信任边界:只接受可信 CDN 和代理传来的地址,保留原始连接信息,验证多级代理链,并让日志、限流和安全策略使用同一套结果。请求头名称可以查文档,过宽的代理信任却会直接留下 IP 伪造入口。
参考资料
- Nginx:《ngx_http_realip_module》《ngx_http_proxy_module》
- Apache HTTP Server:《mod_remoteip》
- Cloudflare:《Restoring original visitor IPs》《HTTP request headers》
- Express:《Express behind proxies》
- RFC 7239:《Forwarded HTTP Extension》
资料核查日期:2026 年 9 月 17 日。CDN 请求头名称、节点 IP 段和回源链路可能调整,请以当前服务商官方文档和控制台配置为准。




