如何在PHP中获取客户端IP地址

如何使用PHP获取客户端IP地址?

我想记录通过他/她的IP地址登录我的网站的用户。

2030503 次浏览

它应该包含在$_SERVER['REMOTE_ADDR']变量中。

$_SERVER['REMOTE_ADDR']实际上可能不包含真实的客户端IP地址,因为它会为例如通过代理连接的客户端提供一个代理地址。这可能不过,这是你真正想要的,这取决于你对IP做了什么。如果你说,试图查看你的流量来自哪里,或者记住用户上次连接的IP,代理或NAT网关的公共IP可能更适合存储,那么某人的私人RFC1918地址可能对你没有任何好处。

有几个像X-Forwarded-For这样的HTTP标头,可能会也可能不会被各种代理设置。问题是这些只是任何人都可以设置的HTTP标头。不能保证它们的内容。$_SERVER['REMOTE_ADDR']是Web服务器接收连接的实际物理IP地址,响应将发送到该地址。其他任何事情都只是任意和自愿的信息。只有一种情况下你可以信任这些信息:你正在控制设置这个标头的代理。这意味着只有当你100%知道标头的设置位置和方式时,你才应该注意它。

话虽如此,这里有一些示例代码:

if (!empty($_SERVER['HTTP_CLIENT_IP'])) {$ip = $_SERVER['HTTP_CLIENT_IP'];} elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];} else {$ip = $_SERVER['REMOTE_ADDR'];}

编者注:使用上面的代码有安全影响。客户端可以将所有HTTP标头信息(即$_SERVER['HTTP_...)设置为它想要的任何值。因此,使用$_SERVER['REMOTE_ADDR']更可靠,因为用户无法设置。

来自:http://roshanbh.com.np/2007/12/getting-real-ip-address-in-php.html

答案是使用#0变量。例如,$_SERVER["REMOTE_ADDR"]将返回客户端的IP地址。

无论您做什么,请确保不要信任从客户端发送的数据。$_SERVER['REMOTE_ADDR']包含连接方的真实IP地址。这是您能找到的最可靠的值。

但是,它们可能位于代理服务器后面,在这种情况下,代理可能设置了$_SERVER['HTTP_X_FORWARDED_FOR'],但此值很容易被欺骗。例如,它可以由没有代理的人设置,或者IP可以是代理后面的LAN的内部IP。

这意味着如果您要保存$_SERVER['HTTP_X_FORWARDED_FOR'],请确保您保存$_SERVER['REMOTE_ADDR']值。例如,将两个值保存在数据库的不同字段中。

如果您要将IP作为字符串保存到数据库中,请确保您至少有45个字符的空间。IPv6在这里停留,并且这些地址大于旧的IPv4地址。

(请注意,IPv6通常最多使用39个字符,但也有一个特殊的IPv4地址的IPv6表示法,其完整形式最多可以使用45个字符。所以如果你知道你在做什么,你可以使用39个字符,但如果你只是想设置并忘记它,请使用45)。

我喜欢这个代码片段:

function getClientIP() {
if (isset($_SERVER)) {
if (isset($_SERVER["HTTP_X_FORWARDED_FOR"]))return $_SERVER["HTTP_X_FORWARDED_FOR"];
if (isset($_SERVER["HTTP_CLIENT_IP"]))return $_SERVER["HTTP_CLIENT_IP"];
return $_SERVER["REMOTE_ADDR"];}
if (getenv('HTTP_X_FORWARDED_FOR'))return getenv('HTTP_X_FORWARDED_FOR');
if (getenv('HTTP_CLIENT_IP'))return getenv('HTTP_CLIENT_IP');
return getenv('REMOTE_ADDR');}

这是我使用的方法,它验证IPv4输入:

// Get user IP addressif ( isset($_SERVER['HTTP_CLIENT_IP']) && ! empty($_SERVER['HTTP_CLIENT_IP'])) {$ip = $_SERVER['HTTP_CLIENT_IP'];} elseif ( isset($_SERVER['HTTP_X_FORWARDED_FOR']) && ! empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];} else {$ip = (isset($_SERVER['REMOTE_ADDR'])) ? $_SERVER['REMOTE_ADDR'] : '0.0.0.0';}
$ip = filter_var($ip, FILTER_VALIDATE_IP);$ip = ($ip === false) ? '0.0.0.0' : $ip;

我最喜欢的解决方案是Zend Framework 2使用的方式。它还考虑了$_SERVER属性HTTP_X_FORWARDED_FORHTTP_CLIENT_IPREMOTE_ADDR,但它声明了一个类来设置一些受信任的代理,并返回一个IP地址而不是数组。我认为这是最接近它的解决方案:

class RemoteAddress{/*** Whether to use proxy addresses or not.** As default this setting is disabled - IP address is mostly needed to increase* security. HTTP_* are not reliable since can easily be spoofed. It can be enabled* just for more flexibility, but if user uses proxy to connect to trusted services* it's his/her own risk, only reliable field for IP address is $_SERVER['REMOTE_ADDR'].** @var bool*/protected $useProxy = false;
/*** List of trusted proxy IP addresses** @var array*/protected $trustedProxies = array();
/*** HTTP header to introspect for proxies** @var string*/protected $proxyHeader = 'HTTP_X_FORWARDED_FOR';
// [...]
/*** Returns client IP address.** @return string IP address.*/public function getIpAddress(){$ip = $this->getIpAddressFromProxy();if ($ip) {return $ip;}
// direct IP addressif (isset($_SERVER['REMOTE_ADDR'])) {return $_SERVER['REMOTE_ADDR'];}
return '';}
/*** Attempt to get the IP address for a proxied client** @see http://tools.ietf.org/html/draft-ietf-appsawg-http-forwarded-10#section-5.2* @return false|string*/protected function getIpAddressFromProxy(){if (!$this->useProxy|| (isset($_SERVER['REMOTE_ADDR']) && !in_array($_SERVER['REMOTE_ADDR'], $this->trustedProxies))) {return false;}
$header = $this->proxyHeader;if (!isset($_SERVER[$header]) || empty($_SERVER[$header])) {return false;}
// Extract IPs$ips = explode(',', $_SERVER[$header]);// trim, so we can compare against trusted proxies properly$ips = array_map('trim', $ips);// remove trusted proxy IPs$ips = array_diff($ips, $this->trustedProxies);
// Any left?if (empty($ips)) {return false;}
// Since we've removed any known, trusted proxy servers, the right-most// address represents the first IP we do not know about -- i.e., we do// not know if it is a proxy server, or a client. As such, we treat it// as the originating IP.// @see http://en.wikipedia.org/wiki/X-Forwarded-For$ip = array_pop($ips);return $ip;}
// [...]}

在此处查看完整代码:https://raw.githubusercontent.com/zendframework/zend-http/master/src/PhpEnvironment/RemoteAddress.php

这是一个更清晰的代码示例,是获取用户IP地址的好方法。

$ip = $_SERVER['HTTP_CLIENT_IP']? $_SERVER['HTTP_CLIENT_IP']: ($_SERVER['HTTP_X_FORWARDED_FOR']? $_SERVER['HTTP_X_FORWARDED_FOR']: $_SERVER['REMOTE_ADDR']);

这是一个使用elvis运算符的较短版本:

$_SERVER['HTTP_CLIENT_IP']? : ($_SERVER['HTTP_X_FORWARDED_FOR']? : $_SERVER['REMOTE_ADDR']);

这是一个使用isset删除通知的版本(谢谢,@shasi kanth):

$ip = isset($_SERVER['HTTP_CLIENT_IP'])? $_SERVER['HTTP_CLIENT_IP']: (isset($_SERVER['HTTP_X_FORWARDED_FOR'])? $_SERVER['HTTP_X_FORWARDED_FOR']: $_SERVER['REMOTE_ADDR']);

像下面这样?

if (($ip=filter_input(INPUT_SERVER, 'REMOTE_ADDR', validate_ip)) === false or empty($ip)) {exit;}echo $ip;

PS

if (($ip=filter_input(INPUT_SERVER, 'REMOTE_ADDR', FILTER_VALIDATE_IP|FILTER_FLAG_NO_PRIV_RANGE|FILTER_FLAG_NO_RES_RANGE)) === false) {header('HTTP/1.0 400 Bad Request');exit;}

所有以“HTTP_”或“X-”开头的标题都可能被欺骗,分别是用户定义的。如果您想跟踪,请使用cookie等。

以下是我找到的最先进的方法,我过去已经尝试过其他一些方法。确保获取访问者的IP地址是有效的(但请注意,任何黑客都可以轻松伪造IP地址)。

function get_ip_address() {
// Check for shared Internet/ISP IPif (!empty($_SERVER['HTTP_CLIENT_IP']) && validate_ip($_SERVER['HTTP_CLIENT_IP'])) {return $_SERVER['HTTP_CLIENT_IP'];}
// Check for IP addresses passing through proxiesif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
// Check if multiple IP addresses exist in varif (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',') !== false) {$iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);foreach ($iplist as $ip) {if (validate_ip($ip))return $ip;}}else {if (validate_ip($_SERVER['HTTP_X_FORWARDED_FOR']))return $_SERVER['HTTP_X_FORWARDED_FOR'];}}if (!empty($_SERVER['HTTP_X_FORWARDED']) && validate_ip($_SERVER['HTTP_X_FORWARDED']))return $_SERVER['HTTP_X_FORWARDED'];if (!empty($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']) && validate_ip($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']))return $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];if (!empty($_SERVER['HTTP_FORWARDED_FOR']) && validate_ip($_SERVER['HTTP_FORWARDED_FOR']))return $_SERVER['HTTP_FORWARDED_FOR'];if (!empty($_SERVER['HTTP_FORWARDED']) && validate_ip($_SERVER['HTTP_FORWARDED']))return $_SERVER['HTTP_FORWARDED'];
// Return unreliable IP address since all else failedreturn $_SERVER['REMOTE_ADDR'];}
/*** Ensures an IP address is both a valid IP address and does not fall within* a private network range.*/function validate_ip($ip) {
if (strtolower($ip) === 'unknown')return false;
// Generate IPv4 network address$ip = ip2long($ip);
// If the IP address is set and not equivalent to 255.255.255.255if ($ip !== false && $ip !== -1) {// Make sure to get unsigned long representation of IP address// due to discrepancies between 32 and 64 bit OSes and// signed numbers (ints default to signed in PHP)$ip = sprintf('%u', $ip);
// Do private network range checkingif ($ip >= 0 && $ip <= 50331647)return false;if ($ip >= 167772160 && $ip <= 184549375)return false;if ($ip >= 2130706432 && $ip <= 2147483647)return false;if ($ip >= 2851995648 && $ip <= 2852061183)return false;if ($ip >= 2886729728 && $ip <= 2887778303)return false;if ($ip >= 3221225984 && $ip <= 3221226239)return false;if ($ip >= 3232235520 && $ip <= 3232301055)return false;if ($ip >= 4294967040)return false;}return true;}

试试这个:

 $_SERVER['REMOTE_ADDR'];
$ip = "";
if (!empty($_SERVER["HTTP_CLIENT_IP"])){// Check for IP address from shared Internet$ip = $_SERVER["HTTP_CLIENT_IP"];}elseif (!empty($_SERVER["HTTP_X_FORWARDED_FOR"])){// Check for the proxy user$ip = $_SERVER["HTTP_X_FORWARDED_FOR"];}else{$ip = $_SERVER["REMOTE_ADDR"];}echo $ip;

好吧,这可以通过使用名为$_SERVERGLOBAL变量来简单地完成。

$_SERVER是一个具有属性名称REMOTE_ADDR的数组。

只需像这样分配它:

$userIp = $_SERVER['REMOTE_ADDR'];

或者像echo $_SERVER['REMOTE_ADDR'];一样直接使用它echo ($_SERVER['REMOTE_ADDR']);

以下函数确定所有可能性并以逗号分隔的格式(ip、ip等)返回值。

它还有一个可选的验证函数(默认情况下禁用的第一个参数),用于根据(私有范围和保留范围)验证IP地址。

<?phpecho GetClientIP(true);
function GetClientIP($validate = False) {$ipkeys = array('REMOTE_ADDR','HTTP_CLIENT_IP','HTTP_X_FORWARDED_FOR','HTTP_X_FORWARDED','HTTP_FORWARDED_FOR','HTTP_FORWARDED','HTTP_X_CLUSTER_CLIENT_IP');
/*Now we check each key against $_SERVER if containing such value*/$ip = array();foreach ($ipkeys as $keyword) {if (isset($_SERVER[$keyword])) {if ($validate) {if (ValidatePublicIP($_SERVER[$keyword])) {$ip[] = $_SERVER[$keyword];}}else{$ip[] = $_SERVER[$keyword];}}}
$ip = ( empty($ip) ? 'Unknown' : implode(", ", $ip) );return $ip;}
function ValidatePublicIP($ip){if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)) {return true;}else {return false;}}

正如所有其他人之前所说,您可以使用$_SERVER['REMOTE_ADDR'];来获取客户端IP地址。

此外,如果您需要有关用户的更多信息,您可以使用以下命令:

<?php$ip = '0.0.0.0';$ip = $_SERVER['REMOTE_ADDR'];$clientDetails = json_decode(file_get_contents("http://ipinfo.io/$ip/json"));echo "You're logged in from: <b>" . $clientDetails->country . "</b>";?>

客户端更具体的信息包含在$client详细信息中。
您可以通过以下方式获取存储在$client详细信息变量中的JSON项目:$clientDetails->邮政编码/主机名/区域/loc…

我使用ipinfo.io来获取额外的信息。

这个功能很紧凑,你可以在任何地方使用它。但是!

不要忘记这一点!在这种类型的函数或代码块中,不能保证记录用户的真实IP地址,因为一些用户可以使用代理或其他安全网关进行不可见或无法跟踪

php函数:

function GetIP(){if ( getenv("HTTP_CLIENT_IP") ) {$ip = getenv("HTTP_CLIENT_IP");} elseif ( getenv("HTTP_X_FORWARDED_FOR") ) {$ip = getenv("HTTP_X_FORWARDED_FOR");if ( strstr($ip, ',') ) {$tmp = explode(',', $ip);$ip = trim($tmp[0]);}} else {$ip = getenv("REMOTE_ADDR");}return $ip;}

用法:

$IP = GetIP();或直接GetIP();

这是一个简单的单线

$ip = $_SERVER['HTTP_X_FORWARDED_FOR']?: $_SERVER['HTTP_CLIENT_IP']?: $_SERVER['REMOTE_ADDR'];

编辑:

上面的代码可能会返回保留地址(如10.0.0.1),所有代理服务器的地址列表,等等。要处理这些情况,请使用以下代码:

function valid_ip($ip) {// for list of reserved IP addresses, see https://en.wikipedia.org/wiki/Reserved_IP_addressesreturn $ip && substr($ip, 0, 4) != '127.' && substr($ip, 0, 4) != '127.' && substr($ip, 0, 3) != '10.' && substr($ip, 0, 2) != '0.' ? $ip : false;}
function get_client_ip() {// using explode to get only client ip from list of forwarders. see https://en.wikipedia.org/wiki/X-Forwarded-Forreturn@$_SERVER['HTTP_X_FORWARDED_FOR'] ? explode(',', $_SERVER['HTTP_X_FORWARDED_FOR'], 2)[0] :@$_SERVER['HTTP_CLIENT_IP'] ? explode(',', $_SERVER['HTTP_CLIENT_IP'], 2)[0] :valid_ip(@$_SERVER['REMOTE_ADDR']) ?:'UNKNOWN';}
echo get_client_ip();

Internet背后有不同类型的用户,因此我们希望从不同部分捕获IP地址。它们是:

1.#0-这包含客户端的真实IP地址。这是您可以从用户那里找到的最可靠的值。

2.#0-这将获取用户正在查看当前页面的主机名。但要使此脚本正常工作,必须配置内部httpd.conf的主机名查找。

3.#0-当用户来自共享的Internet服务时,这将获取IP地址。

4.#0-当用户在代理后面时,这将从用户那里获取IP地址。

因此,我们可以使用以下组合函数从在不同位置查看的用户那里获取真实IP地址,

// Function to get the user IP addressfunction getUserIP() {$ipaddress = '';if (isset($_SERVER['HTTP_CLIENT_IP']))$ipaddress = $_SERVER['HTTP_CLIENT_IP'];else if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))$ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];else if(isset($_SERVER['HTTP_X_FORWARDED']))$ipaddress = $_SERVER['HTTP_X_FORWARDED'];else if(isset($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']))$ipaddress = $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];else if(isset($_SERVER['HTTP_FORWARDED_FOR']))$ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];else if(isset($_SERVER['HTTP_FORWARDED']))$ipaddress = $_SERVER['HTTP_FORWARDED'];else if(isset($_SERVER['REMOTE_ADDR']))$ipaddress = $_SERVER['REMOTE_ADDR'];else$ipaddress = 'UNKNOWN';return $ipaddress;}

用于获取IP地址的安全和警告感知代码段:

$ip = filter_input(INPUT_SERVER, 'HTTP_CLIENT_IP', FILTER_VALIDATE_IP)?: filter_input(INPUT_SERVER, 'HTTP_X_FORWARDED_FOR', FILTER_VALIDATE_IP)?: $_SERVER['REMOTE_ADDR']?? '0.0.0.0'; // Or other value fits "not defined" in your logic

此功能应按预期工作

function Get_User_Ip(){$IP = false;if (getenv('HTTP_CLIENT_IP')){$IP = getenv('HTTP_CLIENT_IP');}else if(getenv('HTTP_X_FORWARDED_FOR')){$IP = getenv('HTTP_X_FORWARDED_FOR');}else if(getenv('HTTP_X_FORWARDED')){$IP = getenv('HTTP_X_FORWARDED');}else if(getenv('HTTP_FORWARDED_FOR')){$IP = getenv('HTTP_FORWARDED_FOR');}else if(getenv('HTTP_FORWARDED')){$IP = getenv('HTTP_FORWARDED');}else if(getenv('REMOTE_ADDR')){$IP = getenv('REMOTE_ADDR');}
//If HTTP_X_FORWARDED_FOR == server ipif((($IP) && ($IP == getenv('SERVER_ADDR')) && (getenv('REMOTE_ADDR')) || (!filter_var($IP, FILTER_VALIDATE_IP)))){$IP = getenv('REMOTE_ADDR');}
if($IP){if(!filter_var($IP, FILTER_VALIDATE_IP)){$IP = false;}}else{$IP = false;}return $IP;}
<?php/*** Function to get the client ip address** @return string The Ip address*/function getIp(): string {if (! empty($_SERVER['HTTP_CLIENT_IP'])) {return $_SERVER['HTTP_CLIENT_IP'];}
if (! empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {return $_SERVER['HTTP_X_FORWARDED_FOR'];}
return $_SERVER['REMOTE_ADDR'] ?? '?';}

甚至更小

/*** Function to get the client ip address** @return string The Ip address*/function getIp(): string {return $_SERVER['HTTP_CLIENT_IP'] ?? $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? '';}

就在这一点上,我很惊讶它还没有被提及,是获取那些坐落在CloudFlare基础设施后面的站点的正确IP地址。它会破坏你的IP地址,并给它们所有相同的值。幸运的是,他们也有一些可用的服务器标头。与其重写已经写好的东西,不如在这里寻找更简洁的答案,是的,我很久以前也经历过这个过程。https://stackoverflow.com/a/14985633/1190051

function get_client_ip(){foreach (array('HTTP_CLIENT_IP','HTTP_X_FORWARDED_FOR','HTTP_X_FORWARDED','HTTP_X_CLUSTER_CLIENT_IP','HTTP_FORWARDED_FOR','HTTP_FORWARDED','REMOTE_ADDR') as $key) {if (array_key_exists($key, $_SERVER)) {foreach (explode(',', $_SERVER[$key]) as $ip) {$ip = trim($ip);if ((bool) filter_var($ip, FILTER_VALIDATE_IP,FILTER_FLAG_IPV4 |FILTER_FLAG_NO_PRIV_RANGE |FILTER_FLAG_NO_RES_RANGE)) {return $ip;}}}}return null;}

或压缩版本:

function get_ip() {foreach (array('HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_X_FORWARDED', 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', 'REMOTE_ADDR') as $key) {if (array_key_exists($key, $_SERVER) === true) {foreach (array_map('trim', explode(',', $_SERVER[$key])) as $ip) {if (filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) !== false) {return $ip;}}}}}

这里有一些代码,应该通过检查各种来源来选择一个有效的IP。

首先,它检查“REMOTE_ADDR”是否是公共IP(而不是您信任的反向代理之一),然后遍历其中一个HTTP标头,直到找到公共IP并返回它。

只要反向代理受信任或服务器直接与客户端连接,它就应该是可靠的。

//Get client's IP or null if nothing looks validfunction ip_get($allow_private = false){//Place your trusted proxy server IPs here.$proxy_ip = ['127.0.0.1'];
//The header to look for (Make sure to pick the one that your trusted reverse proxy is sending or else you can get spoofed)$header = 'HTTP_X_FORWARDED_FOR'; //HTTP_CLIENT_IP, HTTP_X_FORWARDED, HTTP_FORWARDED_FOR, HTTP_FORWARDED
//If 'REMOTE_ADDR' seems to be a valid client IP, use it.if(ip_check($_SERVER['REMOTE_ADDR'], $allow_private, $proxy_ip)) return $_SERVER['REMOTE_ADDR'];
if(isset($_SERVER[$header])){//Split comma separated values [1] in the header and traverse the proxy chain backwards.//[1] https://en.wikipedia.org/wiki/X-Forwarded-For#Format$chain = array_reverse(preg_split('/\s*,\s*/', $_SERVER[$header]));foreach($chain as $ip) if(ip_check($ip, $allow_private, $proxy_ip)) return $ip;}
return null;}
//Check for valid IP. If 'allow_private' flag is set to truthy, it allows private IP ranges as valid client IP as well. (10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)//Pass your trusted reverse proxy IPs as $proxy_ip to exclude them from being valid.function ip_check($ip, $allow_private = false, $proxy_ip = []){if(!is_string($ip) || is_array($proxy_ip) && in_array($ip, $proxy_ip)) return false;$filter_flag = FILTER_FLAG_NO_RES_RANGE;
if(!$allow_private){//Disallow loopback IP range which doesn't get filtered via 'FILTER_FLAG_NO_PRIV_RANGE' [1]//[1] https://www.php.net/manual/en/filter.filters.validate.phpif(preg_match('/^127\.$/', $ip)) return false;$filter_flag |= FILTER_FLAG_NO_PRIV_RANGE;}
return filter_var($ip, FILTER_VALIDATE_IP, $filter_flag) !== false;}

其中之一:

    $ip = $_SERVER['REMOTE_ADDR'];$ip = $_SERVER['HTTP_CLIENT_IP'];$ip = $_SERVER['HTTP_X_FORWARDED_FOR'];$ip = $_SERVER['HTTP_X_FORWARDED'];$ip = $_SERVER['HTTP_FORWARDED_FOR'];$ip = $_SERVER['HTTP_FORWARDED'];

快速解决方案(无错误)

function getClientIP():string{$keys=array('HTTP_CLIENT_IP','HTTP_X_FORWARDED_FOR','HTTP_X_FORWARDED','HTTP_FORWARDED_FOR','HTTP_FORWARDED','REMOTE_ADDR');foreach($keys as $k){if (!empty($_SERVER[$k]) && filter_var($_SERVER[$k], FILTER_VALIDATE_IP)){return $_SERVER[$k];}}return "UNKNOWN";}

要获取客户端IP地址,请使用getenv("REMOTE_ADDR")

例如,

$ip_address = getenv("REMOTE_ADDR");echo $ip_address;

如果您使用localhost调用服务器,它将打印出::1。所以,请使用直接服务器IP地址或域调用您的服务器。

在PHP中,由于太多的安全原因,获取公共IP的最后一个选项应该始终是$_SERVER["REMOTE_ADDR"]

以下是获取客户端经过验证的IP地址的解决方法。

public static function getPublicIP() : string{$realIP = "Invalid IP Address";
$activeHeaders = [];
$headers = ["HTTP_CLIENT_IP","HTTP_PRAGMA","HTTP_XONNECTION","HTTP_CACHE_INFO","HTTP_XPROXY","HTTP_PROXY","HTTP_PROXY_CONNECTION","HTTP_VIA","HTTP_X_COMING_FROM","HTTP_COMING_FROM","HTTP_X_FORWARDED_FOR","HTTP_X_FORWARDED","HTTP_X_CLUSTER_CLIENT_IP","HTTP_FORWARDED_FOR","HTTP_FORWARDED","ZHTTP_CACHE_CONTROL","REMOTE_ADDR" #this should be the last option];
#Find active headersforeach ($headers as $key){if (array_key_exists($key, $_SERVER)){$activeHeaders[$key] = $_SERVER[$key];}}
#Reemove remote address since we got more options to choose fromif(count($activeHeaders) > 1){unset($activeHeaders["REMOTE_ADDR"]);}
#Pick a random item now that we have a secure way.$realIP = $activeHeaders[array_rand($activeHeaders)];
#Validate the public IPif (filter_var($realIP, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)){return $realIP;}
return $realIP;}

如您所见,$_SERVER["REMOTE_ADDR"]是IP的最后一个选项。收到IP后,我们还会验证IP以确保质量和安全性。

如果您不喜欢使用if-else/Switch语句,那么以下解决方案适合您。

function get_client_ip(){$fields = array('HTTP_CF_CONNECTING_IP','HTTP_X_SUCURI_CLIENTIP','HTTP_CLIENT_IP','HTTP_X_FORWARDED_FOR','HTTP_X_FORWARDED','HTTP_FORWARDED_FOR','HTTP_FORWARDED','REMOTE_ADDR',// more custom fields);
foreach ($fields as $ip_field) {if (!empty($_SERVER[$ip_field])) {return $_SERVER[$ip_field];}}
return null;}

您可以使用下面的功能

function get_client_ip() {$ipaddress = '';if (isset($_SERVER['HTTP_CLIENT_IP']))$ipaddress = $_SERVER['HTTP_CLIENT_IP'];else if(isset($_SERVER['HTTP_X_FORWARDED_FOR']))$ipaddress = $_SERVER['HTTP_X_FORWARDED_FOR'];else if(isset($_SERVER['HTTP_X_FORWARDED']))$ipaddress = $_SERVER['HTTP_X_FORWARDED'];else if(isset($_SERVER['HTTP_FORWARDED_FOR']))$ipaddress = $_SERVER['HTTP_FORWARDED_FOR'];else if(isset($_SERVER['HTTP_FORWARDED']))$ipaddress = $_SERVER['HTTP_FORWARDED'];else if(isset($_SERVER['REMOTE_ADDR']))$ipaddress = $_SERVER['REMOTE_ADDR'];else$ipaddress = 'UNKNOWN';return $ipaddress;}

我使用了其他答案之一,并添加了一些额外的东西,如CloudFlare代理和NGINX代理检测。

/*** Gets, validates and returns the connecting client's IP*/function getClientIP(){
// Get real visitor IP behind CloudFlare networkif (!empty($_SERVER["HTTP_CF_CONNECTING_IP"]) && validateIP($_SERVER['HTTP_CF_CONNECTING_IP'])) {return $_SERVER["HTTP_CF_CONNECTING_IP"];}
// Get real visitor IP behind NGINX proxy - https://easyengine.io/tutorials/nginx/forwarding-visitors-real-ip/if (!empty($_SERVER["HTTP_X_REAL_IP"]) && validateIP($_SERVER['HTTP_X_REAL_IP'])) {return $_SERVER["HTTP_X_REAL_IP"];}
// Check for shared Internet/ISP IPif (!empty($_SERVER['HTTP_CLIENT_IP']) && validateIP($_SERVER['HTTP_CLIENT_IP'])) {return $_SERVER['HTTP_CLIENT_IP'];}
// Check for IP addresses passing through proxiesif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
// Check if multiple IP addresses exist in varif (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',') !== false) {$iplist = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);foreach ($iplist as $ip) {if (validateIP($ip))return $ip;}}else {if (validateIP($_SERVER['HTTP_X_FORWARDED_FOR']))return $_SERVER['HTTP_X_FORWARDED_FOR'];}}
if (!empty($_SERVER['HTTP_X_FORWARDED']) && validateIP($_SERVER['HTTP_X_FORWARDED']))return $_SERVER['HTTP_X_FORWARDED'];
if (!empty($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']) && validateIP($_SERVER['HTTP_X_CLUSTER_CLIENT_IP']))return $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'];
if (!empty($_SERVER['HTTP_FORWARDED_FOR']) && validateIP($_SERVER['HTTP_FORWARDED_FOR']))return $_SERVER['HTTP_FORWARDED_FOR'];
if (!empty($_SERVER['HTTP_FORWARDED']) && validateIP($_SERVER['HTTP_FORWARDED']))return $_SERVER['HTTP_FORWARDED'];
// Return unreliable IP address since all else failedreturn $_SERVER['REMOTE_ADDR'];}
/*** Ensures an IP address is both a valid IP address and does not fall within* a private network range.*/function validateIP($ip) {
if (strtolower($ip) === 'unknown')return false;
// Generate IPv4 network address$ip = ip2long($ip);
// Do additional filtering on IPif(!filter_var($ip, FILTER_VALIDATE_IP))return false;
// If the IP address is set and not equivalent to 255.255.255.255if ($ip !== false && $ip !== -1) {
// Make sure to get unsigned long representation of IP address// due to discrepancies between 32 and 64 bit OSes and// signed numbers (ints default to signed in PHP)$ip = sprintf('%u', $ip);
// Do private network range checkingif ($ip >= 0 && $ip <= 50331647)return false;if ($ip >= 167772160 && $ip <= 184549375)return false;if ($ip >= 2130706432 && $ip <= 2147483647)return false;if ($ip >= 2851995648 && $ip <= 2852061183)return false;if ($ip >= 2886729728 && $ip <= 2887778303)return false;if ($ip >= 3221225984 && $ip <= 3221226239)return false;if ($ip >= 3232235520 && $ip <= 3232301055)return false;if ($ip >= 4294967040)return false;}return true;}

我在生产节点中使用它并且它运行良好。由于大部分代码来自这里,我在GNU@https://github.com/d3vdigital/whatsmyip-node下发布了一个节点