GeoIP Testing: MaxMind and PHP

by Gemma Anible on

You want your web application to go global, but how is it supposed to know where your visitors are from? MaxMind offers geographical databases at the city, region and country levels—databases that integrate seamlessly into your existing Apache/PHP setup. Plug your PHP-based web application into MaxMind's free GeoIP database, and simplify your localization project.

MaxMind and You

The key to geo-sensitive web applications is accurately tying user IP addresses and hostnames to their physical locations. The web services and databases available from MaxMind bring that functionality into your application from both the client side (with web services and Javascript client libraries) and the server side (with downloadable databases).

An overview of MaxMind's offering is available on their developer site. This guide will look at integrating both the freely-available GeoLite databases and the subscription-based web services into an application. (Note that the GeoLite databases are updated on the first Tuesday of every month. If you need more frequent updates or better accuracy, you may want to investigate the subscription-based GeoIP2 databases .)

Your Options

You have three options for integrating MaxMind into your Apache/PHP-based web application, and any or all of the options may be a match for your use case.

Apache-only integration

Load geo-location data directly into your server environment. If you need location information about your visitors on every request, this may be your best choice.

PHP integration

Perform ad-hoc location queries against server-side data. If your application spends a lot of time looking up location information for lots of different IPs or hostnames, try this option.

Web service integration

Look up location information as needed, without anything installed locally. If you're only doing location lookups occasionally, you can avoid the overhead of server-based storage with web services.

Server-side Localization With Apache

If your application depends on localization information across the board, you may want to try MaxMind's Apache integration. MaxMind's Apache2 module uses the GeoLite databases to add geographical content, like continent, country, latitude or longitude to the server variables for every request. A full list of the variables that may be populated is available on the MaxMind developer site .

To get started, download MaxMind's free Country and City databases and decompress them:

$ wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
$ wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
$ gunzip GeoIP.dat.gz
$ gunzip GeoLiteCity.dat.gz

Once the files are downloaded and extracted, rename the city database to the filename mod_geoip expects:

mv GeoLiteCity.dat GeoIPCity.dat

You'll need a special module to integrate MaxMind's GeoIP products with Apache2. On Debian-like systems (i.e. Debian, Ubuntu, Mint), the module is available through apt:

$ sudo apt-get install libapache2-mod-geoip

On RPM-based systems (i.e. RHEL, CentOs), you can install the module through yum:

$ sudo yum install mod_geoip

If you are using a different Linux distribution, you may need to build the module yourself. Please see MaxMind's documentation for details.

Once mod_geoip is installed, you're ready for some configuration! We prefer to keep our GeoIP configuration separated from our main Apache2 configuration (we use /etc/apache2/mods-enabled/geoip.conf), but your needs may differ). In the Apache2 configuration file you choose, start by loading the GeoIP module. (Note that on Debian-based systems, the apt package installation may have already loaded mod_geoip for you in /etc/apach2/mods-enabled/geoip.load.)

LoadModule geoip_module /usr/lib/apache2/modules/mod_geoip.so

Now that mod_geoip is loaded, enable it...

<IfModule mod_geoip.c>
GeoIPEnable On
</IfModule>

...and tell Apache where your files are.

<IfModule mod_geoip.c>
GeoIPEnable On
GeoIPDBFile /path/to/GeoIP.dat
GeoIPDBFile /path/to/GeoIPCity.dat
</IfModule>

Full configuration options are documented on MaxMind's developer site.

Finally, reload Apache:

$ sudo service apache2 reload

With the Apache module loaded and configured, your application has access to GeoIP variables on each request. In PHP, you can access these variables in the $_SERVER super-global:

<html>
    <body>
        <h1>Welcome! You are visiting from <?= $_SERVER['GEOIP_CITY'] ?>!</h1>
    </body>
</html>

At WonderProxy, we use MaxMind's Apache integration right on our home page! That big header that tells you where you are comes from a MaxMind server variable, something like this:

<h1>
    You're in <span id="user-location"><?=$_SERVER['GEOIP_REGION_NAME']; ?></span>, your users aren't.
</h1>

(Note that the GEOIP_REGION_NAME variable comes from the non-free edition of MaxMind's database offering.)

Flexible Localization With PHP

The mod_geoip Apache2 module gives you geographical information about each visitor, but what if your application needs geographical information about other sources? Maybe you want to show the top 500 Alexa sites on a map, or show distances between datacenters. PHP's GeoIP library lets you perform ad-hoc location information requests against MaxMind's databases, so you're not limited to visitor-specific details.

PHP's support for MaxMind's GeoIP library comes from PECL, the PHP extension repository. Luckily, Debian- and RPM-based systems make installation relatively painless. On Debian-based systems:

$ sudo apt-get install php5-geoip

Or RPM-based systems:

$ sudo yum install php-pecl-geoip

After installation, you can confirm that PHP's GeoIP functionality is active from the command line:

$ php -i |grep geo
/etc/php5/cli/conf.d/20-geoip.ini,
geoip
geoip support => enabled
geoip extension version => 1.0.7
geoip library version => 1004008
geoip.custom_directory => no value => no value

Now that GeoIP support is enabled in PHP, you can start querying the MaxMind databases for information as you need it. For example, if you want to see where Google's homepage lives:

<?php

$record = geoip_record_by_name('google.com');

echo "Google lives in {$record['city']}, {$record['country_name']}.";

Of course, since the Apache module is still loaded, you can combine visitor geolocation information from Apache with the PHP library:

<?php

$ip = $_SERVER['GEOIP_ADDR']; // Variable injected by mod_geoip
$record = geoip_record_by_name($ip);

echo "Your country is {$record['country_name']} and your city is {$record['city']}.";

WonderNetwork's Geo-testing page is driven entirely by the PHP GeoIP library. We grab your IP address using a server variable:

$ip = $_SERVER['GEOIP_ADDR'];

Then do a full lookup in PHP:

$maxmind = geoip_record_by_name($ip);

$geoip = [
    'country' => $maxmind['country_name'],
    'region' => geoip_region_name_by_code($maxmind['country_code'], $maxmind['region']),
    'city' => $maxmind['city'],
    'lat' => [
        'deg' => latlonToDeg($maxmind['latitude'], true),
        'dec' => $maxmind['latitude']
    ],
    'lon' => [
        'deg' => latlonToDeg($maxmind['longitude'], false),
        'dec' => $maxmind['longitude']
    ],
    'ip' => $ip,
];

Once we have all the information we need, we pour it into that lovely table:

<table id="ip-info">
<tbody>
<tr>
    <td>IP Address</td>
    <td id="user-ip"><?= $geoip['ip'] ?></td>
</tr>
<tr>
    <td>Country</td>
    <td id="user-country"><?= $geoip['country'] ?></td>
</tr>
<tr>
    <td>Region</td>
    <td id="user-region"><?= $geoip['region'] ?></td>
</tr>
<tr>
    <td>City</td>
    <td id="user-city"><?= $geoip['city'] ?></td>
</tr>
<tr>
    <td>Location</td>
    <td id="user-location">
        <?= $geoip['lat']['deg'] ?> / <?= $geoip['lon']['deg'] ?>
    </td>
</tr>
</tbody>

Ad-Hoc Localization With Web Services

Both localization methods discussed so far need MaxMind's GeoIP databases and Apache2 module. That's all well and good if your application depends heavily on localization information, but what if you only need to localize some parts of your application, some of the time? The overhead of server modules and server-side data may be too much to justify. In that case, your application may be a good candidate for MaxMind's subscription-based web services .

Requests to the MaxMind web service require only your license key and the IP address you'd like to query. The web service returns a comma-separated string containing geographical details for the IP address you request. For example, to get organization-level information for Google's 8.8.8.8 DNS server, you could use something like this:

<?php

$license_key = 'YOURKEY';
$ip = '8.8.8.8';

$curl = curl_init();
curl_setopt_array($curl, [
    CURLOPT_URL => "https://geoip.maxmind.com/f?l=$license_key&i=$ip",
    CURLOPT_RETURNTRANSFER => true
]);

$response = curl_exec($curl);
print_r(str_getcsv($response));

At WonderProxy, we use a very similar technique to check the IP addresses associated with our user logins.

Full usage details for the MaxMind web service, including end-point documentation and code samples for multiple languages, is available on the MaxMind developer site. For more about performance tuning GeoIP functionality in Apache and PHP, check out our post on the topic.

Happy globetrotting!