Как получить IP-адрес посетителя сайта в PHP?
Если вы пишите свой модуль статистики, боретесь с подозрительной активностью, например, через блокировку доступа к вашему ресурсу или решаете другую подобную задачу, IP-адрес – это тот параметр, за который можно «зацепиться», ведь он позволяет в какой-то степени отличить одного посетителя сайта от другого.
За свою скромную карьеру программиста я сталкивался с этим вопросом много раз, видел разные реализации описанного и на личном опыте, открыл для себя некоторые моменты, о которых сегодня расскажу в этом посте.
Итак, простое получение IP-адреса пользователя в PHP осуществляется через суперглобальный массив $_SERVER с ключом REMOTE_ADDR:
echo $_SERVER['REMOTE_ADDR'];
Вполне рабочий вариант, но в случае, когда человек использует прокси, REMOTE_ADDR вернет нам не его (человека) реальный IP-адрес, а конечный этого прокси-сервера, через который он попал на сайт. Чтобы учесть это, воспользуйтесь следующей функцией:
function getUserIP() {
if(!empty($_SERVER['HTTP_CLIENT_IP'])) {
$userIP = $_SERVER['HTTP_CLIENT_IP'];
} elseif(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$userIP = $_SERVER['HTTP_X_FORWARDED_FOR'];
} else {
$userIP = $_SERVER['REMOTE_ADDR'];
}
return $userIP;
}
Использование:
echo getUserIP();
Это (в общем) итоговый вариант, а теперь к нюансам.
1. Иногда нам может вернуться не один IP-адрес, а список из нескольких. Можете оставить как есть, это просто уточнение для вас, а можете получить один, например, последний:
$userIP = explode(',', $_SERVER['REMOTE_ADDR']); // Разбиваем список IP-адресов на массив
echo trim(end($userIP)); // Выводим последний элемент (IP-адрес) и удаляем у него пробелы по бокам
В случае с функцией выше эту конструкцию можно написать вместо строчки с return:
$userIP = explode(',', $userIP);
return trim(end($userIP));
2. По-хорошему, особенно если вы работаете с базой данных, полученный результат лучше фильтровать (об этом ниже).
3. Если ваш сайт подключен к Cloudflare, то стоит проверять IP, который он возвращает:
if(isset($_SERVER['HTTP_CF_CONNECTING_IP'])) { // Если сайт подключен к Cloudflare
echo $_SERVER['HTTP_CF_CONNECTING_IP'];
} else {
echo $_SERVER['REMOTE_ADDR'];
}
Выше представленная функция тогда будет иметь следующий вид:
function getUserIP() {
if(!empty($_SERVER['HTTP_CLIENT_IP'])) {
$userIP = $_SERVER['HTTP_CLIENT_IP'];
} elseif(!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) {
$userIP = $_SERVER['HTTP_X_FORWARDED_FOR'];
} elseif(isset($_SERVER['HTTP_CF_CONNECTING_IP'])) { // Если сайт подключен к Cloudflare
$userIP = $_SERVER['HTTP_CF_CONNECTING_IP'];
} else {
$userIP = $_SERVER['REMOTE_ADDR'];
}
return $userIP;
}
Когда я писал свой модуль статистики, именно этот момент «всплыл» у некоторых пользователей, которые начали его использовать.
Фильтрация (валидация) IP-адресов в PHP
Раз уж зашёл разговор о получении IP-адресов, то уместно сделать акцент и на их обработке. В последнее время для себя я взял за правило, что любые данные, которые я получаю извне, нужно фильтровать, поскольку это вопрос не столько правильности работы скрипта, сколько вопрос безопасности.
В PHP на такой случай (фильтрация IP) существует функция filter_var.
Сама по себе функция умеет фильтровать не только IP-адреса. С ней мы уже сталкивались с вами при написании формы обратной связи (там мы с помощью неё фильтровали e-mail).
Простой пример использования:
$userIP = filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP);
if($userIP) {
echo $userIP;
} else {
echo 'IP-адрес невалиден';
}
В ранее написанной нами функции её можно использовать вместо строчки с return:
$userIP = filter_var($userIP, FILTER_VALIDATE_IP);
if($userIP) {
return $userIP;
} else {
return 'IP-адрес невалиден';
}
Это общий вариант использования функции, который проверяет корректность IP-адреса (v4 или v6). Дополнительно можно использовать флаги:
- FILTER_FLAG_IPV4 – IP-адрес формата IPv4;
- FILTER_FLAG_IPV6 – IP-адрес формата IPv6;
- FILTER_FLAG_NO_PRIV_RANGE – IP-адрес не входит в диапазон частных адресов;
- FILTER_FLAG_NO_RES_RANGE – IP-адрес не входит в диапазон зарезервированных адресов.
Пример использования:
$userIP = filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4);
if($userIP) {
echo $userIP;
} else {
echo 'IP-адрес невалиден';
}
Флаги можно совмещать с помощью побитового оператора | (операция ИЛИ):
$userIP = filter_var($_SERVER['REMOTE_ADDR'], FILTER_VALIDATE_IP, FILTER_FLAG_IPV4 | FILTER_FLAG_NO_PRIV_RANGE);
if($userIP) {
echo $userIP;
} else {
echo 'IP-адрес невалиден';
}
Таким образом, мы проверяем, что IP-адрес имеет формат IPv4 и он не входит в диапазон частных адресов.
А вот FILTER_FLAG_IPV4 и FILTER_FLAG_IPV6 между собой совмещать не стоит, поскольку функция это проверяет по умолчанию.