本文约 4,373 字,预计阅读 11 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:所有访客 IP 都一样了
站点接入 Cloudflare、阿里云 CDN,或者在前面加了一层 Nginx 反向代理之后,你可能会发现:
- 后台评论列表里,所有评论的 IP 都是
172.69.x.x或127.0.0.1; - 装了安全插件,封 IP 时把 CDN 节点给封了,导致全站打不开;
- 表单插件记录的地域统计全是服务器机房所在地;
- 限流插件失效,因为所有请求看起来来自同一个地址。
这不是 WordPress 的问题,是网络结构变了但代码没跟着变。
原理分析:REMOTE_ADDR 是谁
PHP 里的 $_SERVER['REMOTE_ADDR'] 是 TCP 连接的对端地址。加了一层代理后,和 PHP-FPM 建立连接的不是访客,而是代理服务器本身,所以 REMOTE_ADDR 永远等于代理的内网 IP。
代理服务器知道访客的真实 IP,它通过 HTTP 头把信息传递下去,业界约定俗成的头字段有:
| 头字段 | 提出者 | 格式 |
|---|---|---|
X-Forwarded-For |
事实标准 | client, proxy1, proxy2(逗号分隔,第一个是真实客户端) |
X-Real-IP |
Nginx 常用 | 单个 IP |
CF-Connecting-IP |
Cloudflare | 单个 IP |
True-Client-IP |
Cloudflare 企业版 / Akamai | 单个 IP |
X-Forwarded-Proto |
— | http 或 https(协议) |
安全风险:X-Forwarded-For 是客户端可伪造的。如果服务器无条件信任它,攻击者可以随意伪造 IP 绕过封禁。所以正确做法是:只信任你自己的代理层,即只接受来自可信代理 IP 的转发头,且剥离客户端自己带的同名头。
保姆级实操
场景一:自建 Nginx 反向代理(前端 Nginx → 后端 Nginx + PHP-FPM)
前端代理配置:
location / {
proxy_pass http://backend;
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;
}
后端站点的 PHP 配置(关键):
# 在 server 块里定义可信代理网段
set_real_ip_from 10.0.0.0/8; # 你的内网 / 代理 IP
set_real_ip_from 127.0.0.1;
real_ip_header X-Forwarded-For;
real_ip_recursive on;
real_ip_recursive on 的作用是:从右往左剥离可信代理的 IP,剩下最左边那个就是真实客户端。这样 PHP 拿到的 $_SERVER['REMOTE_ADDR'] 直接就是真实 IP,所有插件都自动正确,不需要改任何 PHP 代码。这是最优雅的方案。
场景二:Cloudflare(含免费版)
如果源站是 Nginx,用 Cloudflare 官方 IP 段:
# 生成可信代理配置
sudo tee /etc/nginx/conf.d/cloudflare_realip.conf > /dev/null <<'EOF'
EOF
for ip in $(curl -s https://api.cloudflare.com/client/v4/ips | \
C:/Users/Alex/.workbuddy/binaries/python/versions/3.13.12/python.exe -c "import sys,json;d=json.load(sys.stdin);print('\n'.join(d['result']['ipv4_cidrs']))"); do
echo "set_real_ip_from $ip;" | sudo tee -a /etc/nginx/conf.d/cloudflare_realip.conf
done
echo "real_ip_header CF-Connecting-IP;" | sudo tee -a /etc/nginx/conf.d/cloudflare_realip.conf
上面这段建议在 shell 里分步执行,最终文件内容形如:
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 103.21.244.0/22;
# ... 若干条 Cloudflare 段
real_ip_header CF-Connecting-IP;
Cloudflare 的 IP 段会变,建议加个季度更新的 cron:
0 4 1 */3 * curl -s https://api.cloudflare.com/client/v4/ips | \
C:/Users/Alex/.workbuddy/binaries/python/versions/3.13.12/python.exe -c "
import sys,json
d=json.load(sys.stdin)
print('\n'.join('set_real_ip_from %s;'%i for i in d['result']['ipv4_cidrs']))
print('real_ip_header CF-Connecting-IP;')
" > /etc/nginx/conf.d/cloudflare_realip.conf && nginx -t && systemctl reload nginx
场景三:无法改 Nginx(虚拟主机 / 宝塔简易模式)
在 wp-config.php 顶部(必须在 /* That's all */ 之前)加:
/**
* 从代理头还原真实 IP
* 仅在检测到可信代理头时才覆盖,避免本地直连时 IP 变空
*/
if ( ! empty( $_SERVER['HTTP_CF_CONNECTING_IP'] ) ) {
$_SERVER['REMOTE_ADDR'] = $_SERVER['HTTP_CF_CONNECTING_IP'];
} elseif ( ! empty( $_SERVER['HTTP_X_FORWARDED_FOR'] ) ) {
// 取逗号分隔的第一个
$xff = explode( ',', $_SERVER['HTTP_X_FORWARDED_FOR'] );
$xff = trim( $xff[0] );
if ( filter_var( $xff, FILTER_VALIDATE_IP ) ) {
$_SERVER['REMOTE_ADDR'] = $xff;
}
}
同时告诉 WordPress 当前是 HTTPS(否则会出现重定向循环):
if ( ! empty( $_SERVER['HTTP_X_FORWARDED_PROTO'] )
&& 'https' === $_SERVER['HTTP_X_FORWARDED_PROTO'] ) {
$_SERVER['HTTPS'] = 'on';
}
场景四:修复历史数据
已经存进数据库的错误 IP 不会自动更正,可以用 SQL 批量修正(谨慎,先备份):
-- 查看受影响评论数量
SELECT COUNT(*) FROM wp_comments WHERE comment_author_IP = '127.0.0.1';
历史 IP 无法还原(真实 IP 没被记录),只能从 Web 服务器访问日志里反查。所以先修配置再谈数据。
常见踩坑
- 加了代码反而 IP 变成空:
HTTP_X_FORWARDED_FOR在 PHP 里的键名是HTTP_前缀 + 横线变下划线,别写成HTTP_X_FORWARDED_FOR之外的形式;另外本地直连时没有这个头,必须判空。 - 出现重定向循环:只还原了 IP 没处理
HTTPS。Cloudflare 的 “Flexible” 模式下,回源是 HTTP,前端是 HTTPS,WordPress 以为自己没开 HTTPS 就一直 301。用上面的X-Forwarded-Proto判断,或者把 Cloudflare 加密模式改成 “Full (strict)”。 - 封 IP 把 CDN 封了:Wordfence、iThemes Security 之类的插件有”检测代理”选项,默认关闭。开启后插件才会读转发头。否则它封的是 CDN 节点,等于全站封死。
- 案例:某站用了两层代理(外层 CDN,内层 Nginx),
X-Forwarded-For里是真实IP, CDN节点IP。站长在代码里取explode(',')[0]拿到了正确 IP,但安全插件读的是最后一个,结果封禁逻辑全错。多层代理下必须明确”取第几个”,用real_ip_recursive on让 Nginx 帮你算最省心。 - 伪造 IP 绕过限制:任何人都能发
X-Forwarded-For: 8.8.8.8。如果你的站有 IP 白名单功能,务必配合set_real_ip_from限定可信源。
总结
优先级排序:能用 Nginx 的 ngx_http_realip_module 就别改 PHP(一次配置,全站所有插件受益);改不了服务器再退而求其次改 wp-config.php;Cloudflare 用户记得同时处理 X-Forwarded-Proto 防止重定向循环。无论哪种方案,都要遵守”只信任可信代理”这一条安全底线。配好后,去后台看评论列表的 IP 是否已经变成真实地址,再用手机 4G 访问一次确认。关于代理环境下的缓存配合,可参考反向代理与页面缓存的注意事项。
