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

1424

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

我想记录访问我的网站的用户的IP地址。


3
请参阅RFC6302以了解有关记录什么内容的建议,特别是现在要记住记录端口而不仅仅是地址。 - Patrick Mevzek
3
对于那些追踪用户的人,需要注意一点,在全球的一些地区,网络服务提供商正在使用CGNAT技术,这使得仅仅依赖IP地址变得更加复杂,难以信任。 - Jonathan DS
2
函数 getUserIpAddr(){ 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']; } return $ip; } - user3562771
1
你应该使用抽象IP检测。它的价值在于,它可以让你知道IP地址是否在代理或VPN后面,我认为这很重要。他们有一个PHP代码片段,你可以从中复制你的请求。 - johnson23
37个回答

6
以下函数确定所有可能性并以逗号分隔的格式返回值(ip,ip,等)。
它还具有一个可选的验证函数(默认情况下禁用的第一个参数),用于验证IP地址是否在私有范围和保留范围内。
<?php
echo 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;
  }
}

5

<?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'] ?? '';
}

4

试一试这个:

 $_SERVER['REMOTE_ADDR'];

33
这个已经提到过几次了,你的回答实际上没有添加任何有用的东西。 - kenorb
你的回答实际上没有提供任何有用的信息 - 不确定你的意思是什么,它回答了所问的问题。那怎么会不有用呢? - Chris
11
因为他正在回答一个5年前就已经得到大量同样或更好答案的问题。 - Mg Thar

3

我使用了其中一个答案,并添加了一些其他内容,例如CloudFlare代理和NGINX代理检测。

/**
 * Gets, validates and returns the connecting client's IP
 */
function getClientIP(){

    // Get real visitor IP behind CloudFlare network
    if (!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 IP
    if (!empty($_SERVER['HTTP_CLIENT_IP']) && validateIP($_SERVER['HTTP_CLIENT_IP'])) {
        return $_SERVER['HTTP_CLIENT_IP'];
    }

    // Check for IP addresses passing through proxies
    if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {

        // Check if multiple IP addresses exist in var
        if (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 failed
    return $_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 IP
    if(!filter_var($ip, FILTER_VALIDATE_IP))
        return false;

    // If the IP address is set and not equivalent to 255.255.255.255
    if ($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 checking
        if ($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

这会有任何安全隐患吗?在完成上述所有验证后,我能将该值保存到数据库中吗? - sij_a

3

关于这个问题,我很惊讶为什么还没有提到获取那些隐藏在 CloudFlare 基础设施后面的网站的正确 IP 地址。它会破坏你的 IP 地址,并给它们赋相同的值。 幸运的是,他们也有一些服务器头可用。 不要重复已经写过的内容,看看这里得到更简洁的答案,是的,我也很久以前就经历了这个过程。 https://dev59.com/FmUp5IYBdhLWcg3w3ad1#14985633


3

如果你不喜欢使用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;
}

2

在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 headers
        foreach ($headers as $key)
        {
            if (array_key_exists($key, $_SERVER))
            {
                $activeHeaders[$key] = $_SERVER[$key];
            }
        }

         #Reemove remote address since we got more options to choose from
        if(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 IP
        if (filter_var($realIP, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4))
        {
            return $realIP;
        }

        return $realIP;
    }

正如您在这里看到的那样,$_SERVER["REMOTE_ADDR"] 是我们获取IP地址的最后一种选择。在获取IP地址后,我们还会验证IP地址以确保质量和安全性。


2
<?php
/**
 * Get the real IP address of the client
 * 
 * @param array $trusted_proxies list of IP addresses of reverse proxy servers that you trust
 * @return mixed client IP or null
 */
function get_client_ip($trusted_proxies=[]) {
    // In cli mode, there is no remote address
    if (empty($_SERVER['REMOTE_ADDR'])) {
        return null;
    }

    $client_ip = $_SERVER['REMOTE_ADDR'];
    if (!in_array($client_ip, $trusted_proxies)) {
        return $client_ip;
    }

    // The request is coming from a trusted proxy, so we can trust the 
    // "forwarded for" headers
    if (isset($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        return $_SERVER['HTTP_X_FORWARDED_FOR'];
    }

    if (isset($_SERVER['HTTP_CLIENT_IP'])) {
        return $_SERVER['HTTP_CLIENT_IP'];
    }

    // No forwarded client IP header provided; this might be some kind 
    // of health check request. Just return the trusted proxy IP.
    return $client_ip;
}

// Example usage

// if the app doesn't work behind proxy server:
$ip = get_client_ip();

// if you're behind reverse proxy, pass the IP address like this:
$ip = get_client_ip(trusted_proxies: ['10.10.10.10']);

// or like this, for older PHP versions:
$ip = get_client_ip(['10.10.10.10']);


1
这是一个简单的一行代码。
$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_addresses
    return $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-For
    return
    @$_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();

1
这个函数应该按预期工作。
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 ip
    if((($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;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接