最稳妥的做法是在 Nginx 中启用 realip 模块,仅信任 Cloudflare 公布的 IP 段,并读取 CF-Connecting-IP 头部。
先说结论:通过 Nginx 的 ngx_http_realip_module 模块,限制只接受 Cloudflare 节点传来的头部信息,才能准确记录访客真实 IP。
- 适合:已接入 Cloudflare 代理且需要记录源站日志的站点
- 先准备:确保 Nginx 编译了 realip 模块
- 验收:检查 access.log 中记录的 IP 是否为访客真实 IP
1. 确认模块支持
执行命令查看 Nginx 编译参数,确认包含 http_realip_module:
nginx -V 2>&1 | grep 'http_realip_module'如果没有输出,说明当前 Nginx 不支持,需要重新编译或安装包含该模块的版本(如 nginx-full 包)。
2. 自动化更新 IP 段脚本(推荐)
Cloudflare IP 段会不定期调整,手动复制容易遗漏导致部分流量无法获取真实 IP。建议编写脚本自动拉取最新列表并生成配置文件。
创建脚本 update_cf_ips.sh:
#!/bin/bash
# 配置路径
CONF_FILE="/etc/nginx/conf.d/cloudflare_realip.conf"
TEMP_FILE="/tmp/cloudflare_ips.tmp"
# 拉取最新 IP 段
curl -s https://www.cloudflare.com/ips-v4 > $TEMP_FILE
curl -s https://www.cloudflare.com/ips-v6 >> $TEMP_FILE
# 生成 Nginx 配置
echo "# Auto-generated Cloudflare IPs - $(date)" > $CONF_FILE
while IFS= read -r line; do
[ -z "$line" ] && continue
echo "set_real_ip_from $line;" >> $CONF_FILE
done < $TEMP_FILE
echo "real_ip_header CF-Connecting-IP;" >> $CONF_FILE
echo "real_ip_recursive on;" >> $CONF_FILE
# 清理并重载
rm -f $TEMP_FILE
nginx -t && nginx -s reload赋予执行权限并运行:
chmod +x update_cf_ips.sh
./update_cf_ips.sh建议将此脚本加入 crontab 定期执行,确保 IP 段最新。
3. 编写 Nginx 配置
在 nginx.conf 的 http 块中引入生成的配置文件:
http {
include /etc/nginx/conf.d/cloudflare_realip.conf;
# 其他配置...
}注意:不要直接在配置中硬编码少量 IP 段,务必使用完整列表或 include 自动生成的文件。
4. 验证是否生效
1. 查看访问日志
访问一次网站,然后查看 Nginx access.log,确认记录的 IP 不是 Cloudflare 的 IP 段,而是你的本地出口 IP。
tail -f /var/log/nginx/access.log2. 程序层面验证
如果是 PHP 网站,可以通过 $_SERVER['REMOTE_ADDR'] 查看。配置生效前该值通常为 Cloudflare IP,生效后应为真实 IP。
常见坑与安全警告
- 严禁信任所有 IP:不要将
set_real_ip_from设置为0.0.0.0/0,否则攻击者可以伪造任意头部 IP,导致日志污染或绕过安全策略。 - IP 列表完整性:若未使用自动脚本,务必核对 Cloudflare 官方最新列表,遗漏 IP 段会导致该节点流量无法获取真实 IP。
- IPv6 支持:如果站点支持 IPv6,记得同时配置 ips-v6 列表,否则 IPv6 访客的真实 IP 无法识别。
- 多层代理:如果 Cloudflare 后面还有其他代理,可能需要开启
real_ip_recursive on,通常只接 CF 也需要开启以防万一。
参考来源
- Cloudflare 官方文档 - IP Addresses (https://www.cloudflare.com/ips/)
- Nginx 官方文档 - ngx_http_realip_module (https://nginx.org/en/docs/http/ngx_http_realip_module.html)