Как в PHP получить IP адрес пользователя: от простого к сложному

Раздел: PHP программирование -> Сетевые функции PHP

Способы получения IP адреса пользователя в PHP

Наиболее надёжное и универсальное решение

Для получения IP адреса посетителя в веб-приложении чаще всего используют суперглобальный массив $_SERVER. Прямой способ - обратиться к ключу REMOTE_ADDR, который содержит IP, с которым установлено TCP-соединение. Однако при работе через прокси-серверы, балансировщики нагрузки или CDN (например, Cloudflare) необходимо учитывать дополнительные заголовки.


<?php
// Базовое получение IP (без учёта прокси)
$ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
echo $ip;
?>
  

Php ip сервера (ip сервера в php)

Этот код подходит для простых сайтов без посредников. Проблема: если пользователь заходит через анонимный прокси, то REMOTE_ADDR вернёт адрес прокси, а не реальный IP клиента. Для обхода этого применяют анализ заголовков HTTP_X_FORWARDED_FOR и HTTP_X_REAL_IP.

Типичные ошибки и способы их решения

  • Подделка заголовков. Злоумышленник может отправить произвольный X-Forwarded-For. Решение: доверять только проверенным прокси (например, внутренним IP) и валидировать полученный адрес.
  • Несколько IP в заголовке. В X-Forwarded-For может быть цепочка адресов. Первый элемент считается клиентским. Решение: извлекать первый IP из строки, разделённой запятыми.
  • IPv6 адреса. Некоторые функции (например, ip2long) неприменимы к IPv6. Решение: использовать filter_var с флагами FILTER_FLAG_IPV4 или FILTER_FLAG_IPV6.

<?php
// Продвинутый способ с учётом прокси и валидацией
function getClientIP() {
    $keys = [
        'HTTP_X_FORWARDED_FOR',
        'HTTP_X_REAL_IP',
        'HTTP_CLIENT_IP',
        'REMOTE_ADDR'
    ];
    foreach ($keys as $key) {
        if (isset($_SERVER[$key])) {
            $ipList = explode(',', $_SERVER[$key]);
            $ip = trim($ipList[0]); // берём первый IP
            if (filter_var($ip, FILTER_VALIDATE_IP)) {
                return $ip;
            }
        }
    }
    return '0.0.0.0';
}
echo getClientIP();
?>
  

Php get ip (получить ip пользователя в php)

Этот метод перебирает заголовки в порядке убывания доверия и возвращает первый валидный IP. Используется в большинстве современных фреймворков.

Как получить IP без учёта прокси-серверов?

Если сайт не использует посредников, достаточно простого обращения к $_SERVER['REMOTE_ADDR']. Это самый быстрый способ, так как не требует обработки дополнительных заголовков.


<?php
$ip = $_SERVER['REMOTE_ADDR'];
echo 'Ваш IP: ' . $ip;
?>
  

Проблема: при использовании локального сервера (127.0.0.1) или при тестировании через VPN будет отображаться адрес последнего узла. Для разработки это приемлемо, но на production может потребоваться более точная информация.

Как получить реальный IP за списком анонимных прокси?

Когда запрос проходит через несколько прокси, заголовок X-Forwarded-For может содержать цепочку адресов: клиент, прокси1, прокси2. Первый элемент является IP клиента. Следует учитывать, что этот заголовок легко подделывается, поэтому ему доверяют только при наличии белого списка внутренних прокси.


<?php
$forwarded = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? '';
$ips = explode(',', $forwarded);
$clientIP = trim($ips[0]);
if (filter_var($clientIP, FILTER_VALIDATE_IP)) {
    echo 'Клиентский IP: ' . $clientIP;
} else {
    echo 'Невалидный IP';
}
?>
  

Риск подделки. Если прокси не настроен на удаление старого заголовка X-Forwarded-For, злоумышленник может подставить ложный IP. Решение: фильтровать только доверенные прокси по белым спискам IP.

Как защититься от подделки IP через заголовки?

Для повышения безопасности можно использовать комбинацию REMOTE_ADDR и проверки, что запрос пришёл от известного прокси. Например, если сайт работает за Nginx, настроенным как reverse proxy, то REMOTE_ADDR будет равен IP Nginx, а настоящий IP передаётся в заголовке X-Real-IP. В PHP нужно доверять только этому заголовку, если он пришёл с IP самого Nginx.


<?php
$trustedProxies = ['192.168.1.1', '10.0.0.1']; // IP внутренних прокси
if (in_array($_SERVER['REMOTE_ADDR'], $trustedProxies)) {
    $ip = $_SERVER['HTTP_X_REAL_IP'] ?? $_SERVER['REMOTE_ADDR'];
} else {
    $ip = $_SERVER['REMOTE_ADDR'];
}
echo $ip;
?>
  

Сложность - необходимо заранее знать все IP доверенных прокси. При масштабировании список может меняться.

Как получить IP в консольном скрипте (CLI)?

При запуске PHP из командной строки массив $_SERVER может быть пустым или содержать другие ключи. IP пользователя в CLI не имеет смысла, но иногда требуется определить собственный IP сервера. Для этого используют функции gethostname() и gethostbyname().


<?php
$host = gethostname();
$localIP = gethostbyname($host);
echo 'Локальный IP: ' . $localIP;
?>
  

Ограничение: gethostbyname() возвращает только IPv4. Для получения всех IP нужно использовать gethostbynamel().

Как получить IP при использовании Cloudflare?

Cloudflare добавляет заголовок HTTP_CF_CONNECTING_IP, который содержит настоящий IP посетителя. Этот заголовок считается надёжным, так как Cloudflare его проверяет. При этом REMOTE_ADDR будет равен одному из IP Cloudflare.


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

Важно: использовать этот заголовок только если сайт однозначно работает через Cloudflare. Иначе он может быть подделан.

Расширенные примеры работы с IP в PHP

Ниже приведены более сложные и специализированные примеры, демонстрирующие различные подходы к получению и валидации IP адресов.

Пример 1: Функция с приоритетным выбором заголовков и фильтрацией приватных адресов

Пример

<?php
function getPublicIP() {
    $headers = [
        'HTTP_CF_CONNECTING_IP',
        'HTTP_X_FORWARDED_FOR',
        'HTTP_X_REAL_IP',
        'REMOTE_ADDR'
    ];
    $privateRanges = [
        '10.0.0.0/8',
        '172.16.0.0/12',
        '192.168.0.0/16',
        '127.0.0.0/8'
    ];
    foreach ($headers as $h) {
        if (!empty($_SERVER[$h])) {
            $ipList = explode(',', $_SERVER[$h]);
            foreach ($ipList as $ip) {
                $ip = trim($ip);
                if (filter_var($ip, FILTER_VALIDATE_IP)) {
                    // Проверка, что IP не приватный
                    $isPrivate = false;
                    foreach ($privateRanges as $range) {
                        if (ip_in_range($ip, $range)) {
                            $isPrivate = true;
                            break;
                        }
                    }
                    if (!$isPrivate) {
                        return $ip;
                    }
                }
            }
        }
    }
    return '0.0.0.0';
}

function ip_in_range($ip, $range) {
    list($subnet, $bits) = explode('/', $range);
    if ($bits === null) $bits = 32;
    $ipLong = ip2long($ip);
    $subnetLong = ip2long($subnet);
    $mask = -1 << (32 - $bits);
    $subnetLong &= $mask;
    return ($ipLong & $mask) === $subnetLong;
}

echo getPublicIP();
?>

Результат выполнения на сервере с реальным внешним IP:

93.180.12.34

Пояснение: функция проходит по заголовкам, извлекает все IP из цепочек и возвращает первый не приватный адрес. Это особенно полезно, когда за прокси могут скрываться внутренние IP.

Пример 2: Проверка валидности IPv4 и IPv6 с помощью filter_var

Пример

<?php
$testIPs = ['192.168.1.1', '::1', '256.0.0.1', '2001:db8::1'];
foreach ($testIPs as $ip) {
    $isValid = filter_var($ip, FILTER_VALIDATE_IP);
    echo "{$ip} - " . ($isValid ? 'валидный' : 'невалидный') . "\n";
}
?>
192.168.1.1 - валидный
::1 - валидный
256.0.0.1 - невалидный
2001:db8::1 - валидный

Пояснение: filter_var с флагом FILTER_VALIDATE_IP корректно распознаёт оба формата и отбрасывает заведомо некорректные адреса.

Пример 3: Извлечение первого IP из строки X-Forwarded-For с регулярным выражением

Пример

<?php
$headerValue = '203.0.113.42, 10.0.0.5, 172.16.0.1';
preg_match('/\b(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\b/', $headerValue, $matches);
$firstIP = $matches[1] ?? 'Не удалось извлечь';
echo $firstIP;
?>
203.0.113.42

Пояснение: регулярное выражение ищет первое вхождение IPv4 адреса. Альтернатива - explode и trim, но regex быстрее, если нужен только первый адрес.

Пример 4: Получение IP из окружения при использовании Docker или Kubernetes

Пример

<?php
// В некоторых контейнерных средах IP клиента может быть в переменной окружения
$ipFromEnv = getenv('CLIENT_IP') ?: '';
if (filter_var($ipFromEnv, FILTER_VALIDATE_IP)) {
    echo 'IP из окружения: ' . $ipFromEnv;
} else {
    $ip = $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
    echo 'IP из REMOTE_ADDR: ' . $ip;
}
?>

Пояснение: некоторые оркестраторы (например, Traefik) передают реальный IP через переменные окружения или дополнительные заголовки. Пример показывает резервный вариант.

Пример 5: Логирование всех заголовков, связанных с IP, для отладки

Пример

<?php
$headers = [
    'REMOTE_ADDR',
    'HTTP_X_FORWARDED_FOR',
    'HTTP_X_REAL_IP',
    'HTTP_CF_CONNECTING_IP',
    'HTTP_CLIENT_IP'
];
$logData = [];
foreach ($headers as $h) {
    $logData[$h] = $_SERVER[$h] ?? 'не определён';
}
error_log(json_encode($logData, JSON_UNESCAPED_UNICODE));
echo 'Данные записаны в лог';
?>

Результат (в логе):

{"REMOTE_ADDR":"192.168.1.100","HTTP_X_FORWARDED_FOR":"93.180.12.34","HTTP_X_REAL_IP":"93.180.12.34","HTTP_CF_CONNECTING_IP":"не определён","HTTP_CLIENT_IP":"не определён"}

Пояснение: такой подход помогает диагностировать, какие заголовки доступны в конкретной инфраструктуре, и выбрать правильную стратегию.

получить IP пользователя в PHP - comments

En
Php get ip (php)