Налаштування Geo-IP визначення регіону користувача на сайті

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Налаштування Geo-IP визначення регіону користувача на сайті
Середня
від 1 робочого дня до 3 робочих днів
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Налаштування Geo-IP визначення регіону користувача на сайту

Визначення регіону по IP — фундамент для персоналізації контенту, регіонального ціноутворення та аналітики. Завдання виглядає простою, але в деталях багато нюансів: точність баз даних, IPv6, прокси та VPN, кешування, обновлення даних.

Вибір бази даних GeoIP

MaxMind GeoLite2 — безкоштовна база, вимагає реєстрації та ліцензійного ключа. Обновляется щовівторка. Точність по містам для Росії — близько 80%, по країнах — 98%+.

MaxMind GeoIP2 City — платна версія із підвищеною точністю та включеними ISP-даними. Оправдана для e-commerce з регіональним ціноутворенням.

ip-api.com / ipinfo.io — HTTP API, без локальної бази. Підходит для малого навантаження (до кількох тисяч запитів на день). Додає затримку ~50–200 мс на кожен перший візит.

Для продакшн-сайту з будь-якою значимою навантаженням — лише локальна база MaxMind.

Встановлення MaxMind GeoLite2

# Встановлення geoipupdate для автоматичних обновлень бази
apt-get install geoipupdate

# /etc/GeoIP.conf
AccountID 123456
LicenseKey ваш_ключ_із_особистого_кабінету
EditionIDs GeoLite2-City GeoLite2-Country

# Первинна загрузка
geoipupdate

# Cron: щоду в 3:00 (база обновляется по вівторках)
0 3 * * 3 /usr/bin/geoipupdate

База зберігається в /var/lib/GeoIP/GeoLite2-City.mmdb.

PHP: пакет geoip2/geoip2

composer require geoip2/geoip2
// app/Services/GeoIpService.php
use GeoIp2\Database\Reader;
use GeoIp2\Exception\AddressNotFoundException;

class GeoIpService
{
    private Reader $reader;

    public function __construct()
    {
        $this->reader = new Reader(config('geoip.database_path'));
    }

    public function lookup(string $ip): array
    {
        // Не ищемо по RFC1918 адресам — це інтранет
        if ($this->isPrivateIp($ip)) {
            return $this->defaultResult();
        }

        try {
            $record = $this->reader->city($ip);
            return [
                'country_code' => $record->country->isoCode,         // 'RU'
                'country_name' => $record->country->name,             // 'Russia'
                'region_code'  => $record->subdivisions[0]?->isoCode, // 'MOW'
                'region_name'  => $record->subdivisions[0]?->name,    // 'Moscow'
                'city'         => $record->city->name,                 // 'Moscow'
                'latitude'     => $record->location->latitude,
                'longitude'    => $record->location->longitude,
                'timezone'     => $record->location->timeZone,        // 'Europe/Moscow'
                'is_vpn'       => false, // GeoLite2 не визначає VPN
            ];
        } catch (AddressNotFoundException) {
            return $this->defaultResult();
        }
    }

    private function isPrivateIp(string $ip): bool
    {
        return filter_var($ip, FILTER_VALIDATE_IP,
            FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false;
    }

    private function defaultResult(): array
    {
        return [
            'country_code' => config('geoip.default_country'),
            'region_name'  => null,
            'city'         => null,
            'timezone'     => config('app.timezone'),
        ];
    }
}

Отримання реального IP за прокси/балансувальником

Nginx та хмарні балансувальники передають оригінальний IP через заголовки. Важливо довіряти лише відомим IP балансувальників:

// config/trustedproxies.php або bootstrap/app.php (Laravel 11)
->withMiddleware(function (Middleware $middleware) {
    $middleware->trustProxies(
        proxies: ['10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16'],
        headers: Request::HEADER_X_FORWARDED_FOR
    );
})

Cloudflare передає реальний IP в заголовку CF-Connecting-IP — його потрібно обробити окремо:

public function getClientIp(Request $request): string
{
    // Cloudflare
    if ($cf = $request->header('CF-Connecting-IP')) {
        return $cf;
    }
    return $request->ip();
}

Кешування результатів

Обращение до бази MMDB швидке (~0.1 мс), але для високонавантажених сайтів кешуємо в Redis по IP:

public function lookupCached(string $ip): array
{
    return Cache::remember(
        "geoip:{$ip}",
        86400, // 24 години
        fn() => $this->lookup($ip)
    );
}

Для додатків з тисячами унікальних IP за годину — кеш Redis з TTL 24 ч дасть відчутний прирост. Для звичайних сайтів — достатньо просто не робити lookup при кожному запиту одного користувача (зберігати в сесії).

Зберігання в сесії

// app/Http/Middleware/DetectUserRegion.php
public function handle(Request $request, Closure $next): Response
{
    if (!$request->session()->has('geo')) {
        $ip  = app(GeoIpService::class)->getClientIp($request);
        $geo = app(GeoIpService::class)->lookupCached($ip);
        $request->session()->put('geo', $geo);
    }

    View::share('userGeo', $request->session()->get('geo'));

    return $next($request);
}

Після першого запиту дані лежать в сесії — база GeoIP не опрашується повторно.

IPv6

MaxMind GeoLite2 підтримує IPv6. Єдиний нюанс — PHP-функція filter_var коректно обробляє IPv6, але isPrivateIp потрібно доповнити діапазонами:

private function isPrivateIp(string $ip): bool
{
    // Локальні IPv6 адреси
    if (str_starts_with($ip, '::1') || str_starts_with($ip, 'fc') || str_starts_with($ip, 'fd')) {
        return true;
    }
    return filter_var($ip, FILTER_VALIDATE_IP,
        FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false;
}

Точність та обмеження

GeoLite2 визначає місто в ~75–85% випадків для користувачів з Росії. Для великих міст точність вища. Користувачі через VPN або корпоративні прокси будуть визначені по IP вихідного вузла — не за реальним місцезнаходженням.

Для критичних сценаріїв (показ регіональних цін) варто передбачити ручний вибір регіону з збереженням в куку — користувач завжди може скорегувати визначення. Це одночасно вирішує проблему VPN-користувачів і відповідає очікуванням аудиторії.

Обновлення бази

База GeoLite2 обновляется по вівторках. geoipupdate в cron забезпечує актуальність без ручного втручання. При обновленні бази рекомендується скинути Redis-кеш GeoIP (Cache::tags(['geoip'])->flush() при використанні тегованого кешу).