我们通过修改C:\Windows\System32\drivers\etc 的hosts:
127.0.0.1 abc.com那么打开浏览器访问 http://abc.com,就可以看看到index.asp:
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<%
Response.Write "REMOTE_ADDR=" & Request.ServerVariables("REMOTE_ADDR") & "<br>"
Response.Write "REMOTE_HOST=" & Request.ServerVariables("REMOTE_HOST") & "<br>"
Response.Write "SERVER_NAME=" & Request.ServerVariables("SERVER_NAME") & "<br>"
Response.Write "HTTP_HOST=" & Request.ServerVariables("HTTP_HOST") & "<br>"
%>显示的网页是:
REMOTE_ADDR=127.0.0.1
REMOTE_HOST=127.0.0.1
SERVER_NAME=abc.com
HTTP_HOST=abc.comREMOTE_ADDR 它包含客户端的真实IP地址,这是您能从用户那里找到的最可靠的值。
REMOTE_HOST 这将获取用户查看当前页面的主机名。但要使此脚本正常工作,必须在httpd.conf内部配置主机名查找。
HTTP_CLIENT_IP 和 REMOTE_ADDR 都能拿到客户端 IP,但它们来源完全不同,**不能互换,也不能简单取一个就完事**——多数情况下直接用 REMOTE_ADDR 最可靠,而 HTTP_CLIENT_IP 几乎总是不可信、易被伪造。
为什么 HTTP_CLIENT_IP 基本不可信
这个值来自 HTTP 请求头中的 X-Forwarded-For 或 X-Real-IP(具体取决于 Web 服务器配置),但 PHP 并不自动解析这些头为 HTTP_CLIENT_IP;它只是把原始请求头里叫 Client-IP 的字段原样抄过来——而这个字段完全由客户端或中间代理控制。
浏览器、curl、Postman 都能手动加 Client-IP: 1.2.3.4 头,$_SERVER['HTTP_CLIENT_IP'] 就会返回这个假 IP
Nginx/Apache 默认根本不会设置 Client-IP 头,所以该变量常为空或不存在
即使你写了 proxy_set_header Client-IP $remote_addr;,也只是在反向代理时“主动设”了它,不代表它安全——上游若没校验,仍可能被绕过
REMOTE_ADDR 是唯一真实可靠的连接端 IP
这是 PHP 从底层 socket 连接中读取的对端地址,由操作系统提供,**无法被 HTTP 请求头篡改**。它代表与当前 Web 服务器(或 PHP-FPM)建立 TCP 连接的那个 IP。
直连场景下,就是用户真实出口 IP(如家庭宽带公网 IP)
有反向代理(Nginx → PHP-FPM)时,REMOTE_ADDR 是 Nginx 的内网 IP(如 127.0.0.1 或 192.168.1.10),不是用户 IP —— 这是正常现象,不是 bug
想在这种架构下拿到用户真实 IP,必须靠代理显式传递(如 X-Forwarded-For),再由 PHP 主动读取并校验,而不是依赖 HTTP_CLIENT_IP
真正安全获取用户 IP 的做法(带校验)
别拼凑 HTTP_CLIENT_IP、HTTP_X_FORWARDED_FOR、REMOTE_ADDR 然后直接用。得先确认哪些 IP 是可信的(比如你的 Nginx 服务器内网段),再从可信链中提取最左/最后一个非私有 IP。
