114

I'm using this PHP code to get a visitor's IP address:

<?php echo $_SERVER['REMOTE_ADDR']; ?>

But, I can't get the real IP address from visitors when they are using a proxy. Is there any way to get a visitor's IP address in this case?

reformed
  • 4,505
  • 11
  • 62
  • 88
  • 3
    You can only get the real ip from a proxy if that proxy is not fully anonymous otherwise all u will get is the proxy ip – Prix Nov 30 '12 at 14:00
  • You can see this link http://allinonescript.blogspot.in/2016/09/how-to-get-real-ip-isp-country-city-and-etc-from-visitor-using-php.html – Vadivel S Sep 19 '16 at 09:54
  • Check this function: https://gist.github.com/RyadPasha/c025ddbc4a389d32917f05afde9001ea – RyadPasha Nov 23 '21 at 18:54
  • Proxies like VPNs exist to hide the real IP address of the user. You are not able to get the real IP address of the current user if they are using them. – Henryc17 Apr 23 '22 at 03:08

9 Answers9

216

Try this php code.

<?PHP

function getUserIP()
{
    // Get real visitor IP behind CloudFlare network
    if (isset($_SERVER["HTTP_CF_CONNECTING_IP"])) {
              $_SERVER['REMOTE_ADDR'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
              $_SERVER['HTTP_CLIENT_IP'] = $_SERVER["HTTP_CF_CONNECTING_IP"];
    }
    $client  = @$_SERVER['HTTP_CLIENT_IP'];
    $forward = @$_SERVER['HTTP_X_FORWARDED_FOR'];
    $remote  = $_SERVER['REMOTE_ADDR'];

    if(filter_var($client, FILTER_VALIDATE_IP))
    {
        $ip = $client;
    }
    elseif(filter_var($forward, FILTER_VALIDATE_IP))
    {
        $ip = $forward;
    }
    else
    {
        $ip = $remote;
    }

    return $ip;
}


$user_ip = getUserIP();

echo $user_ip; // Output IP address [Ex: 177.87.193.134]


?>
Mario
  • 1,374
  • 6
  • 22
  • 48
60

This is the most common technique I've seen:

function getUserIP() {
    if( array_key_exists('HTTP_X_FORWARDED_FOR', $_SERVER) && !empty($_SERVER['HTTP_X_FORWARDED_FOR']) ) {
        if (strpos($_SERVER['HTTP_X_FORWARDED_FOR'], ',')>0) {
            $addr = explode(",",$_SERVER['HTTP_X_FORWARDED_FOR']);
            return trim($addr[0]);
        } else {
            return $_SERVER['HTTP_X_FORWARDED_FOR'];
        }
    }
    else {
        return $_SERVER['REMOTE_ADDR'];
    }
}

Note that it does not guarantee it you will get always the correct user IP because there are many ways to hide it.

Sri Harsha Chilakapati
  • 11,744
  • 6
  • 50
  • 91
Teneff
  • 30,564
  • 13
  • 72
  • 103
  • 1
    Upvoted. This is one of the few solutions that properly handles the `HTTP_X_FORWARDED_FOR` header, which may contain a comma-separated list if the request passed through one or more proxies (according to https://en.wikipedia.org/wiki/X-Forwarded-For#Format) – Sean the Bean Mar 14 '18 at 13:38
  • 1
    Since it's possible to spoof the `HTTP_X_FORWARDED_FOR` header, it's a good idea to test it with `filter_var(trim($addr), FILTER_VALIDATE_IP)` to make sure you at least have a valid IP address before returning it. – Sean the Bean Mar 14 '18 at 13:52
  • Does this go in functions.php or wp-config.php? – Garconis Apr 06 '19 at 18:24
  • 2
    @Garconis no one haven't mentioned WordPress here – Teneff Apr 06 '19 at 19:12
  • 1
    Here's a PHP 7.x+ version of the above, including the `filter_var()` addition: `function get_user_IP(): string {` `$ip = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'] ?? '';` `if (strpos($ip, ',') > 0) {` `$addr = explode(',', $ip);` `return filter_var(trim($addr[0]), FILTER_VALIDATE_IP);` `}` `return $ip;` `}` [Sorry for the messed-up formatting — I can't post a block of code in here, apparently, just inline code.] Also, if you're using PHP 8.x+ you can replace `strpos($ip, ',') > 0` with `str_contains($ip, ',')` – Jordan Bradford Nov 17 '22 at 17:34
19

This is my approach:

 function getRealUserIp(){
    switch(true){
      case (!empty($_SERVER['HTTP_X_REAL_IP'])) : return $_SERVER['HTTP_X_REAL_IP'];
      case (!empty($_SERVER['HTTP_CLIENT_IP'])) : return $_SERVER['HTTP_CLIENT_IP'];
      case (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) : return $_SERVER['HTTP_X_FORWARDED_FOR'];
      default : return $_SERVER['REMOTE_ADDR'];
    }
 }

How to use:

$ip = getRealUserIp();
El cero
  • 607
  • 5
  • 13
  • 28
    This is a very strange and confusing use of a switch statement. http://www.codethinked.com/dont-be-clever – Andrew Ensley Jun 10 '16 at 20:46
  • 9
    What do you consider confusing exactly? For me, it is quite clear! – El cero Jun 16 '16 at 18:32
  • 8
    It's confusing because this is exactly what if/elseif is meant to be used for. – Andy Dec 19 '16 at 15:32
  • 9
    If you ever find yourself using switch(true), stop, and use an if statement(s). How can this possibly be considered clearer than the accepted if/else? – DJ Far Jan 24 '17 at 02:46
  • 1
    I think the advantage (?) of a switch statement here is the way it's pre-processed in most languages to be run as a jump table and, in cases of large data or frequent use, can save on processing. However if it's not going to be run at a high frequency, clarity should not be sacrificed for processing speed. – Xandor Apr 10 '19 at 00:43
  • 1
    I'd discourage this as an antipattern. Only constant cases can be reduced to a jump table. Otherwise, as here, every case is guaranteed to be tested until a break/return/etc is hit. So while this is more verbose and less readable than an elseif ladder, it's otherwise functionally identical. – Dewi Morgan Oct 03 '21 at 06:10
  • For me: `code` private function getRealUserIp() { if(!empty($_SERVER['HTTP_X_REAL_IP'])) { return $_SERVER['HTTP_X_REAL_IP']; } 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']; }`code` is shorter and clearer – bolvo Jan 30 '22 at 10:29
6

Proxies may send a HTTP_X_FORWARDED_FOR header but even that is optional.

Also keep in mind that visitors may share IP addresses; University networks, large companies and third-world/low-budget ISPs tend to share IPs over many users.

Halcyon
  • 57,230
  • 10
  • 89
  • 128
  • 1
    The header "specification" can handle multiple proxies, the chain of ips will be comma separated in the header value – Esailija Nov 30 '12 at 14:00
  • Just one note. Third world ISP does opposite. They create dynamic ip for each login. So its multiple ips per user and not one ip for multiple user. – itachi Nov 30 '12 at 14:15
4

apply this code for get the ipaddress:

    if (getenv('HTTP_X_FORWARDED_FOR')) { $pipaddress = getenv('HTTP_X_FORWARDED_FOR');
 $ipaddress = getenv('REMOTE_ADDR'); 
    echo "Your Proxy IP address is : ".$pipaddress. "(via $ipaddress)" ; } 
    else { $ipaddress = getenv('REMOTE_ADDR'); echo "Your IP address is : $ipaddress"; }
    ------------------------------------------------------------------------
Arpit Patel
  • 7,212
  • 5
  • 56
  • 67
Yogesh Prajapati
  • 251
  • 3
  • 13
4

This is my function.

benefits :

  • Work if $_SERVER was not available.
  • Filter private and/or reserved IPs;
  • Process all forwarded IPs in X_FORWARDED_FOR
  • Compatible with CloudFlare
  • Can set a default if no valid IP found!
  • Short & Simple !

/**
 * Get real user ip
 *
 * Usage sample:
 * GetRealUserIp();
 * GetRealUserIp('ERROR',FILTER_FLAG_NO_RES_RANGE);
 * 
 * @param string $default default return value if no valid ip found
 * @param int    $filter_options filter options. default is FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE
 *
 * @return string real user ip
 */

function GetRealUserIp($default = NULL, $filter_options = 12582912) {
    $HTTP_X_FORWARDED_FOR = isset($_SERVER)? $_SERVER["HTTP_X_FORWARDED_FOR"]:getenv('HTTP_X_FORWARDED_FOR');
    $HTTP_CLIENT_IP = isset($_SERVER)?$_SERVER["HTTP_CLIENT_IP"]:getenv('HTTP_CLIENT_IP');
    $HTTP_CF_CONNECTING_IP = isset($_SERVER)?$_SERVER["HTTP_CF_CONNECTING_IP"]:getenv('HTTP_CF_CONNECTING_IP');
    $REMOTE_ADDR = isset($_SERVER)?$_SERVER["REMOTE_ADDR"]:getenv('REMOTE_ADDR');

    $all_ips = explode(",", "$HTTP_X_FORWARDED_FOR,$HTTP_CLIENT_IP,$HTTP_CF_CONNECTING_IP,$REMOTE_ADDR");
    foreach ($all_ips as $ip) {
        if ($ip = filter_var($ip, FILTER_VALIDATE_IP, $filter_options))
            break;
    }
    return $ip?$ip:$default;
}
Ehsan Chavoshi
  • 681
  • 6
  • 10
1

If the Proxy is which you trust, you can try: (Assume the Proxy IP is 151.101.2.10)

<?php

$trustProxyIPs = ['151.101.2.10'];

$clientIP  = isset($_SERVER['REMOTE_ADDR']) ? $_SERVER['REMOTE_ADDR'] : NULL;

if (in_array($clientIP, $trustProxyIPs)) {

    $headers = ['HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR'];

    foreach ($headers as $key => $header) {

        if (isset($_SERVER[$header]) && filter_var($_SERVER[$header], FILTER_VALIDATE_IP)) {

            $clientIP = $_SERVER[$header];

            break;
        }
    }
}

echo $clientIP;

This will prevent forged forward header by direct requested clients, and get real IP via trusted Proxies.

Nick Tsai
  • 3,799
  • 33
  • 36
-1

Yes, $_SERVER["HTTP_X_FORWARDED_FOR"] is how I see my ip when under a proxy on my nginx server.

But your best bet is to run phpinfo() on a page requested from under a proxy so you can look at all the availabe variables and see what is the one that carries your real ip.

Nelson
  • 49,283
  • 8
  • 68
  • 81
-6

This works for Windows and Linux! It doesn't matter if it's localhost or online..

    function getIP() {
    $ip = $_SERVER['SERVER_ADDR'];

    if (PHP_OS == 'WINNT'){
        $ip = getHostByName(getHostName());
    }

    if (PHP_OS == 'Linux'){
        $command="/sbin/ifconfig";
        exec($command, $output);
        // var_dump($output);
        $pattern = '/inet addr:?([^ ]+)/';

        $ip = array();
        foreach ($output as $key => $subject) {
            $result = preg_match_all($pattern, $subject, $subpattern);
            if ($result == 1) {
                if ($subpattern[1][0] != "127.0.0.1")
                $ip = $subpattern[1][0];
            }
        //var_dump($subpattern);
        }
    }
    return $ip;
}
Luke Stevenson
  • 10,357
  • 2
  • 26
  • 41
Valcone
  • 59
  • 2
  • 15
  • 1
    That doesn't work every time because there are more OS than Win and Linux. Chek this: http://stackoverflow.com/a/738893/2127296 – Iván Rodríguez Torres Feb 12 '16 at 13:27
  • 1
    You're right, the solution just work for linux and win. :) – Valcone Feb 12 '16 at 13:29
  • 10
    This returns the server's IP address. Not the visitor's. The question asked for the visitor's IP address. – Andrew Ensley Jun 10 '16 at 20:41
  • Downvoted because this returns the SERVER's IP address, not the VISITOR's IP address. – S. Saad Feb 04 '17 at 02:07
  • 1
    Except the fact that as other said, it returns the server IP, while the question clearly is different, it uses `exec()` and is too complicated without a reason. Getting the visitor's IP does not require so much. Even if you use it to get the server IP, it should be 3 short lines of code to cover windows servers as well as linux. – durduvakis Jun 22 '17 at 09:31