Определить IP-адрес пользователя — базовая задача для любого сайта. Но на практике всё не так просто: VPN, прокси и балансировщики нагрузки часто подменяют реальный IP и передают в PHP не то, что вы ожидаете.

Что происходит

По умолчанию в PHP чаще всего берут IP из $_SERVER['REMOTE_ADDR']. Но если клиент сидит за прокси или балансировщиком (Nginx, Cloudflare, HAProxy) — этот IP будет адресом прокси, а не пользователя.

Чтобы узнать реальный адрес, смотрят специальные заголовки:

  • X-Forwarded-For — цепочка IP-адресов всех прокси.
  • X-Real-IP — часто содержит исходный IP клиента.
  • X-Forwarded — реже, но встречается.

Простой пример функции:

function getRealIpAddress() {
   if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
       $ipAddresses = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
       return trim(end($ipAddresses));
   } elseif (!empty($_SERVER['HTTP_X_REAL_IP'])) {
       return $_SERVER['HTTP_X_REAL_IP'];
   } elseif (!empty($_SERVER['HTTP_X_FORWARDED'])) {
       return $_SERVER['HTTP_X_FORWARDED'];
   }
   
   return $_SERVER['REMOTE_ADDR'];
}

$realIpAddress = getRealIpAddress();
echo "Реальный IP адрес пользователя: " . $realIpAddress;

Но есть нюанс

Заголовки X-Forwarded-For легко подделать. Злоумышленник может отправить любой IP. Поэтому такие заголовки стоит доверять только если их добавляет ваш прокси или балансировщик. Без доверенного прокси — использовать их рискованно.

Пример с доверенными прокси:

function getRealIpAddress($trustedProxies = []) {

    if (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
        $ipAddresses = explode(',', $_SERVER['HTTP_X_FORWARDED_FOR']);
        $ipAddress = trim(end($ipAddresses));

        if (in_array($ipAddress, $trustedProxies)) {
            return $ipAddress;
        }
    }

    return $_SERVER['REMOTE_ADDR'];
}


$trustedProxies = ['192.168.1.1', '192.168.1.2']; 
$realIpAddress = getRealIpAddress($trustedProxies);
echo "Реальный IP адрес пользователя: " . $realIpAddress;

Если используете Cloudflare

При использовании Cloudflare можно настроить получение реального IP адреса  с помощью nginx или Apache так, чтобы они сразу пробрасывали реальный IP в $_SERVER['REMOTE_ADDR']. Тогда не придётся разбирать заголовки вручную.

Итог

  • Не доверяйте заголовкам без настройки доверенного прокси.
  • Проверяйте цепочку IP.
  • Если есть Cloudflare или балансировщик — правильно настройте проброс.