20

What is the most accurate way to get user's IP address in 2017 via PHP?

I've read a lot of SO questions and answers about it, but most of answers are old and commented by users that these ways are unsafe.

For example, take a look at this question (2011): How to get the client IP address in PHP?

Tim Kennedy's answer contains a recommendation to use something like:

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

But as I've read a lot, I have seen that to use X_FORWARDED_FOR is unsafe, as the comment below highlights:

Do NOT use the above code unless you know EXACTLY what it does! I've seen MASSIVE security holes due to this. The client can set the X-Forwarded-For or the Client-IP header to any arbitrary value it wants. Unless you have a trusted reverse proxy, you shouldn't use any of those values.

As I didn't know EXACTLY what it does, I don't want to take the risk. He said it is unsafe, but did not provide a safe method to get user's IP address.

I've tried the simple $_SERVER['REMOTE_ADDR'];, but this returns the wrong IP. I've tested this and my real IP follows this pattern: 78.57.xxx.xxx, but I get an IP address like: 81.7.xxx.xxx

So do you have any ideas?

Infinity
  • 828
  • 4
  • 15
  • 41
  • 81.7.xxx.xxx ... its Your Public Ip ... and 78.57.xxx.xxx its your pc ip. – RïshïKêsh Kümar May 20 '17 at 11:04
  • https://www.google.co.in/search?q=my+ip&oq=myip&aqs=chrome.1.69i57j0l5.2290j0j7&sourceid=chrome&ie=UTF-8 – RïshïKêsh Kümar May 20 '17 at 11:05
  • @RïshïKêshKümar so yes, my IP is like 78.57...., but `$_SERVER['REMOTE_ADDR'];` resturns me like `81.7.....` – Infinity May 20 '17 at 11:06
  • $_SERVER['REMOTE_HOST'] .... try this ... what you get – RïshïKêsh Kümar May 20 '17 at 11:08
  • Before using IPs for anything you should really know what they are and how IP networks work. IPs are just an implementation detail of a data transport mechanism (TCP/IP). It doesn't necessarily give you useful information beyond some detail on how the data of the current request was transported to your server. Only once you understand that should you consider using IPs for any other purpose. – deceze May 26 '17 at 10:17
  • Well you would have to ask the device itself and hopefully it will tell you. – Robert May 27 '17 at 15:25
  • Sorry to break the news to you. Nothing has changed that the Internet is aware of. However you could always use some algorithms to detect whether the user is lying or behind a Proxy. e.g. locale mismatch – Chibueze Opata May 28 '17 at 17:28

10 Answers10

20

Short answer:

$ip = $_SERVER['REMOTE_ADDR'];


As of 2021 (and still) $_SERVER['REMOTE_ADDR']; is the only reliable way to get users ip address, but it can show erroneous results if behind a proxy server.
All other solutions imply security risks or can be easily faked.

Pedro Lobito
  • 94,083
  • 31
  • 258
  • 268
  • Yes, and `$_SERVER['REMOTE_ADDR']` returning the proxy IP is not wrong or incorrect; just not what you really want from it. – Narf Oct 02 '18 at 10:44
  • 1
    Um, what 4 days delay? I've posted my answer hours before yours ... And I didn't comment because I didn't "like" a word, but because your usage of that word propagates a falsehood. – Narf Oct 20 '18 at 23:25
  • This is for shared hosting only. – Brian Fegter May 04 '19 at 14:59
11

From a security POV, nothing but $_SERVER['REMOTE_ADDR'] is reliable - that's just the simple truth, unfortunately.

All the variables prefixed with HTTP_ are in fact HTTP headers sent by the client, and there there's no other way to transfer that information while requests pass through different servers.
But that of course automatically means that clients can spoof those headers.

You can never, ever trust the client.

Unless it is you ... If you control the proxy or load-balancer, it is possible to configure it so that it drops such headers from the original request.
Then, and only then, you could trust an e.g. X-Client-IP header, but really, there's no need to at that point ... your webserver can also be configured to replace REMOTE_ADDR with that value and the entire process becomes transparent to you.

This will always be the case, no matter which year we are in ... for anything related to security - only trust REMOTE_ADDR.
Best case scenario is to read the HTTP_ data for statistical purposes only, and even then - make sure that the input is at least a valid IP address.

Narf
  • 14,600
  • 3
  • 37
  • 66
  • Thank you for an answer, but I'm thinking If It is safe at all to save `'HTTP_X_FORWARDED..` to database, everyone tells that It's unsafe and can be spoofed. So If user will change `HTTP_X...` to value like`'` or anything like this, It could be SQL Injected? – Infinity May 23 '17 at 12:37
  • 1
    Yes, it can ... That's why you need to validate that it is a valid IP address. You can do that easily with `filter_var()` and `FILTER_VALIDATE_IP`. – Narf May 23 '17 at 12:43
  • @Infinity SQL injection is an entirely separate topic that you must guard against by properly forming SQL queries, not by being afraid of what characters any one specific value may or may not contain. – deceze May 26 '17 at 06:53
7

You have to collaborate with your sysops team (or if you're wearing that hat too, you will need to do some research). The header check is used when your network infrastructure is configured in certain ways where the remote requester is one of your network appliances instead of the end user.

This sort of thing happens when your web server(s) sit behind a load balancer or firewall or other appliance that needs to interrogate the payload to properly handle it. An example is when a load balancer terminated ssl and forwards the request on to the web server without ssl. When this occurs the remote address becomes the load balancer. It also happens with firewall appliances that do the same thing.

Most instances the device will offer configuration to set a header value in the request with the original remote ip address. The header is usually what you'd expect but it can in some cases be different or even configurable.

What's more, depending on your web server configuration (apache, nginx or other) may not support or be currently configured to support certain custom headers such as the common ip header.

The point is us you will need to investigate your network configuration to ensure that the original requester's ip makes it all the way through to your application code and in what form.

Jeremy Giberson
  • 1,063
  • 8
  • 15
5

If you'd like to use a pre-built library, you can use Whip.

Using pre-made libraries are usually better because most of them will have been checked thoroughly by an active community. Some of them, especially the ones that have been around for a long time, have more features built-in.

But if you want to code it yourself to learn the concept, then it's ok I guess. I recommend packaging it as a stand alone library and releasing it as open-source :)

EDIT: I do not recommend using the remote IP in security mechanisms as they are not always reliable.

aljo f
  • 2,430
  • 20
  • 22
4

First, it is impossible to reliably determine someone's source IP address if they are intent on being hidden. Even something which today seems foolproof, will soon have a loophole (if it doesn't already). As such, any answer below should be considered UNTRUSTED, which means that if you put all of your eggs in this basket, be prepared for someone to take advantage of it or circumvent it somehow.

I won't get in to all the ways someone can circumvent IP tracking, because it is constantly evolving. What I will say is that it can be a useful tool for logging as long as you know that IP addresses can easily change or otherwise be masked.

Now, one big point to make is that there is a difference between a public IP address and a private IP address. In IPV4, routers are generally assigned one public IP address, which is all that a server-side language can actually grab, because it doesn't see your client-side IP address. To a server, your computer doesn't exist as a web-space. Instead, your router is all that matters. In turn, your router is the only thing that cares about your computer, and it assigns a private IP address (to which your 172...* address belongs) to make this work. This is good for you, because you can't directly access a computer behind a router.

If you want to access a private IP address, you need to use JavaScript (client-side language). You can then store the data asynchronously via AJAX. As far as I know, this is only currently possible using WebRTC-enabled Chrome and Firefox. See here for a demo.

I tested this and it returns private IP addresses. Typically I think this is used by advertisers to help track individual users in a network, in conjunction with the public IP address. I am certain that it will quickly become useless as people come up with workarounds or as public outcry forces them to offer the ability to disable the WebRTC API. However, for the time being it works for anyone who has JavaScript enabled on Chrome and Firefox.

More Reading:

smcjones
  • 5,490
  • 1
  • 23
  • 39
3

Get Client IP Address:

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

Note:: This would work only on live site, because on your local host your ip would be one (1) of the internal ip addresses, like 127.0.0.1 So, its Return ::1

Example : https://www.virendrachandak.com/demos/getting-real-client-ip-address-in-php.php

Its Show Your Local Ip: Like ... 78.57.xxx.xxx

Example:

<?php
$myIp= getHostByName(php_uname('n'));
 echo $myIp;
?>
RïshïKêsh Kümar
  • 4,734
  • 1
  • 24
  • 36
  • But question is about user IP, not local – u_mulder May 20 '17 at 11:19
  • You are right it is, but this answer is still valid because a user and your server could feasibly be within the same private LAN and so not routing over a public network so the remote_addr would be one of your own. – CodingInTheUK Oct 07 '17 at 23:13
3

As the real method is to check user IP is $ip = $_SERVER['REMOTE_ADDR'];

If the user is using VPN or any proxy then it will not detect the original IP.

Narayan
  • 1,670
  • 1
  • 19
  • 37
2

How about this one -

public function getClientIP()
    {
        $remoteKeys = [
            'HTTP_X_FORWARDED_FOR',
            'HTTP_CLIENT_IP',
            'HTTP_X_FORWARDED',
            'HTTP_FORWARDED_FOR',
            'HTTP_FORWARDED',
            'REMOTE_ADDR',
            'HTTP_X_CLUSTER_CLIENT_IP',
        ];

        foreach ($remoteKeys as $key) {
            if ($address = getenv($key)) {
                foreach (explode(',', $address) as $ip) {
                    if ($this->isValidIp($ip)) {
                        return $ip;
                    }
                }
            }
        }

        return '127.0.0.0';
    }


    private function isValidIp($ip)
    {
        if (!filter_var($ip, FILTER_VALIDATE_IP,
                FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE)
            && !filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV6 | FILTER_FLAG_NO_PRIV_RANGE)
        ) {
            return false;
        }

        return true;
    }
toothful
  • 882
  • 3
  • 15
  • 25
1

I use this code, and it works for me. Take a look to it.

<?php

// Gets client's IP.
$ip = getenv("HTTP_CLIENT_IP")?:
getenv("HTTP_X_FORWARDED_FOR")?:
getenv("HTTP_X_FORWARDED")?:
getenv("HTTP_FORWARDED_FOR")?:
getenv("HTTP_FORWARDED")?:
getenv("REMOTE_ADDR");

echo $ip;

?>

Here, a working example. Hope it helps!

KAZZABE
  • 197
  • 2
  • 10
1
  1. Because of different network setups (proxy servers, private networks, etc.) and how administrators configure their networks, it is difficult to obtain the client IP address. Standards are being addressed related to this issue.

  2. The following function worked in 4 different tests (Home Network, VPN, Remote connection, public internet). The code can be used as base code for your project. Modify as needed.

  3. The function does validate the IP address, but does not validate IP ranges. This would be an additional test after you obtain the client IP.

  4. $_SERVER["REMOTE_ADDR"] does not always return the true client IP address.

  5. Because some of the parameters can be set by end users, security can be an issue.

Set Client IP address

$clientIpAddress = $du->setClientIpAddress($_SERVER);

public function setClientIpAddress($serverVars) {
    # Initialization
    $searchList = "HTTP_CLIENT_IP,HTTP_X_FORWARDED_FOR,HTTP_X_FORWARDED,HTTP_X_CLUSTER_CLIENT_IP,HTTP_FORWARDED_FOR,HTTP_FORWARDED,REMOTE_ADDR";
    $clientIpAddress = "";

    # Loop through parameters
    $mylist = explode(',', $searchList);
    foreach ($mylist as $myItem) {
        # Is our list set?
        if (isset($serverVars[trim($myItem)])) {
            # Loop through IP addresses
            $myIpList = explode(',', $serverVars[trim($myItem)]);
            foreach ($myIpList as $myIp) {
                if (filter_var(trim($myIp), FILTER_VALIDATE_IP)) {
                    # Set client IP address
                    $clientIpAddress = trim($myIp);

                    # Exit loop
                    break;
                }
            }
        }

        # Did we find any IP addresses?
        if (trim($clientIpAddress) != "") {
            # Exit loop
            break;
        }
    }

    # Default (if needed)
    if (trim($clientIpAddress) == "") {
        # IP address was not found, use "Unknown"
        $clientIpAddress = "Unknown";
    }

    # Exit
    return $clientIpAddress;
}
Beckham
  • 11
  • 3