PHP: How to detect / get the real client IP address of website visitors?

It may seem simple at first because most of us should be relying on the server side environmental variable REMOTE_ADDR solely for client IP addresses:

echo $_SERVER['REMOTE_ADDR'];

Yet it’s barely enough to get the real IP for a variety of circumstances such as when the user is visiting your website from a proxy server. To everyone’s surprise, there are a lot more environmental variables regarding client IP address than just the most straightforward one, REMOTE_ADDR. Consider this snippet in the attempt to detect the real source IP address of the request:

function get_ip_address() {
    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 (explode(',', $_SERVER[$key]) as $ip) {
                if (filter_var($ip, FILTER_VALIDATE_IP) !== false) {
                    return $ip;
                }
            }
        }
    }
}

It first searches through a series of possible environmental variables that may contain the client IP address and uses whichever that is set and then extract the potential IP value to be validated. After successful validation by the PHP5 filter_var() function, the value is returned. You better not change the order these variable names are placed in the literal array.

This approach is much more sophisticated than just looking at REMOTE_ADDR but it’s far from mess-proof because it relies on the HTTP header information which can be easily manipulated anywhere along the way the request is routed to your server / website.

19 comments

  1. Pingback: PHP Security Checklist for Websites and Web Applications – Bottom Line for Every Good PHP Developer

  2. Omoba

    I got you stuff, really looks nice and tactical. But I tried to call the function this way:
    echo get_ip_address(); but generated this error: Fatal error: Call to undefined function filter_var() in C:\wamp\www\php-bank\create-directory\index.php on line 10, That is the error that it generated, please help! I would love to get the reply in my mail box please! Thanks.

    • Yang Yang

      You may want to upgrade to the latest PHP version. :) filter_var() is a recent addition to the native functions.

  3. Alphonse

    $client_addr = filter_var((!empty($_SERVER[‘HTTP_CLIENT_IP’]))? $_SERVER[‘HTTP_CLIENT_IP’]:
    (!empty($_SERVER[‘HTTP_X_FORWARDED_FOR’]))? $_SERVER[‘HTTP_X_FORWARDED_FOR’]:
    (!empty($_SERVER[‘REMOTE_ADDR’]))? $_SERVER[‘REMOTE_ADDR’]:’UNKNOWN’, FILTER_SANITIZE_STRING);

    • Yang Yang

      This is not preferable because it jeopardizes the readability of the code.

      Human efficiency comes way higher priority than that of machines or storage.

      • Ifreelancer.asia

        The Above code given by ‘Alphonse’ is an awesome simple code, and is more better than yours, while taking on reference to Server.

    • urorbit

      i like your code :)
      thanx

  4. Marcel

    Thanks for script, I’ll try, it seems very useful.

  5. niko

    This is the most elegant code I’ve seen for the job.
    Nice work!

  6. Robin Jakobsson

    Very nice snippet, thanks a lot!

  7. Steve

    Hello, nice job here! I totally agree with Yang Yang on readability being the most important thing. Actually, for the sake of it, 2 extra things I’d do on your function are:
    – use a regex and avoid one extra level of indentation (on the filter_var line)
    – declare the array before the foreach (might have some speed benefits also, if an array is otherwise build at every cycle iteration – not sure on this; main point is nevertheless to be able to break long constant line in multiple ones, though, so o improve readability);
    But hey, again, cool solution of yours 😉

  8. en4ce

    thanks! that works great

  9. Rutger

    ip-spoofing get’s realy easy with this, just adding an extra header in enough.

  10. anshu

    [ $client_addr = filter_var((!empty($_SERVER[‘HTTP_CLIENT_IP’]))? $_SERVER[‘HTTP_CLIENT_IP’]:
    (!empty($_SERVER[‘HTTP_X_FORWARDED_FOR’]))? $_SERVER[‘HTTP_X_FORWARDED_FOR’]:
    (!empty($_SERVER[‘REMOTE_ADDR’]))? $_SERVER[‘REMOTE_ADDR’]:’UNKNOWN’, FILTER_SANITIZE_STRING) ]

  11. Pingback: Hỏi về $_SERVER['REMOTE_ADDR'] - Trang 4

  12. phphunger

    wow simply superb post…

  13. errorare

    thanks for great works.

  14. sanaan Barzinji

    well done,

    Sanaan Barzinji
    Erbil, Iraq