根本原因是Nginx未正确配置set_real_ip_from和real_ip_header,导致$remote_addr始终为代理IP而非真实客户端IP;必须精确设置可信代理网段、启用real_ip_header、确保配置位于http块顶层并重载生效。

Hyperf 项目在 Nginx 反向代理后拿不到真实 IP,根本原因不是框架问题,而是 Nginx 没有把可信的客户端 IP 信息正确“注入”并“声明可信来源”,导致 $remote_addr 始终是 Nginx 自身的连接 IP(如 127.0.0.1 或 Docker 网络地址),Hyperf 读 $_SERVER['REMOTE_ADDR'] 或 $request->getServerParams()['remote_addr'] 自然就错了。
nginx 配置中 set_real_ip_from 必须精确匹配代理来源 IP 段
很多人直接写 set_real_ip_from 0.0.0.0/0;,看似“全放行”,实则触发安全降级:Nginx 会拒绝更新 $remote_addr,因为该配置等价于“所有请求都不可信”,real_ip_header 失效。
- 必须根据实际部署环境填写真实的上游代理网段 —— 比如 Docker 默认桥接网络通常是
172.17.0.0/16或172.18.0.0/16,K8s Service 网段可能是10.96.0.0/12,云厂商 SLB 则需查文档确认其出口 IP 段 - 若用多级代理(例如 CDN → Nginx → Hyperf),
set_real_ip_from只需填**最直接上游**(即你的 Nginx 所连接的那层代理)的 IP 段,不是终端用户或 CDN 的 IP - 可临时用
log_format debug '$remote_addr — $http_x_forwarded_for — $realip_remote_addr';配合access_log查看原始$remote_addr是什么,再反推该填哪个网段
proxy_set_header 和 real_ip_header 必须成对生效
只在 location 块里写 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 不够,Hyperf 要读这个头,Nginx 还得主动“认它”——靠 real_ip_header X-Forwarded-For; 告诉自己:“这个头里存的是真 IP”。否则 $remote_addr 不变,Hyperf 的 getServerParams()['remote_addr'] 就永远不是你想要的。
-
proxy_set_header X-Real-IP $remote_addr;是给后端代码读的快捷头,但 Nginx 自身不据此更新$remote_addr;而real_ip_header X-Real-IP;才会让 Nginx 把它当真 - 推荐统一用
X-Forwarded-For:它支持多级追加($proxy_add_x_forwarded_for会自动拼接),配合real_ip_recursive on;可从列表末尾向前找第一个非可信 IP - 务必确认
real_ip_header和proxy_set_header中的 header 名大小写一致(HTTP 头名不区分大小写,但 Nginx 配置严格匹配字符串)
Hyperf 中读取逻辑不能只 fallback 到 remote_addr
很多项目写个 get_client_ip() 函数,按 X-Forwarded-For → X-Real-IP → remote_addr 顺序取,看似稳妥,实则埋雷:如果 Nginx 没配 set_real_ip_from,remote_addr 就是 Nginx 自己的 IP;而 X-Forwarded-For 又可能被恶意伪造(比如客户端直接发 X-Forwarded-For: 1.2.3.4)。
- 真正安全的做法是:只信任 Nginx 解析后的
$remote_addr(它已由real_ip_header+set_real_ip_from校验过),其他头一律忽略 - Hyperf 中应优先使用
$request->getServerParams()['remote_addr'],而不是手动解析 headers —— 因为remote_addr在 Nginx 正确配置后已是真实 IP - 若必须读 header(如兼容旧逻辑),至少加白名单校验:
if (in_array($clientIp, $trustedProxies)) { /* discard */ }
最容易被忽略的一点:Nginx 的 real_ip_header 和 set_real_ip_from 必须放在 http{} 块顶层,不能只写在某个 server 或 location 里;且修改后必须 nginx -t && nginx -s reload,SIGHUP 不会重载 realip 模块配置。











