网站接入 CDN 后访客 IP 全变成节点 IP?X-Forwarded-For、代理信任与日志恢复教程

接入 CDN 后,源站默认记录节点 IP 是正常现象。本文说明 X-Forwarded-For 多级代理链的含义,给出 Nginx realip、Apache mod_remoteip 和应用 trust proxy 的安全配置方法,并介绍日志验证、伪造头测试和源站访问限制。
网站接入 CDN 后访客 IP 全变成节点 IP?X-Forwarded-For、代理信任与日志恢复教程

网站接入 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_addrREMOTE_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 段和回源链路可能调整,请以当前服务商官方文档和控制台配置为准。

云服务商实操指南

云服务器流量包用完后会怎样?超额计费、带宽限速与账单告警指南

2026-9-17 15:10:35

知识库

[选型指南] 避免踩坑:选择服务器提供商时要考察的10个关键点

2025-4-29 10:14:27