Following is a quick set of instructions to set up city-level location detection using Maxmind’s GeoIP library using Apache/PHP.

There’re multiple ways to do it. Frankly the trickiest part is figuring-out the most appropriate one. I think the most efficient approach in cost/performance terms is to install C API and then let mod_geoip Apache module interface with it, getting parsed values as part of the $_SERVER variable on the PHP side. There’re some wrapper PHP libraries on top of C library, as well as pure-PHP libraries, but the former did not really work, while the latter is supposed to be much slower than C lib/mod_geoip.

So here we go:

Install GeoIP C API

$ mkdir ~/src
$ cd src
$ wget http://geolite.maxmind.com/download/geoip/api/c/GeoIP.tar.gz
$ tar xzvf GeoIP.tar.gz 
$ cd GeoIP-1.4.8
$ ./configure 
$ make
$ sudo make install

Install Apache GeoIP module

$ cd ..
$ wget http://geolite.maxmind.com/download/geoip/api/mod_geoip2/mod_geoip2_1.2.7.tar.gz
$ tar xzvf mod_geoip2_1.2.7.tar.gz 
$ cd mod_geoip2_1.2.7
$ sudo /usr/sbin/apxs -i -a -L/usr/local/lib -I/usr/local/include -lGeoIP -c mod_geoip.c

At the very end, you should get an output like:

[activating module `geoip' in /etc/httpd/conf/httpd.conf]

Please note the Apache config file’s path and in that file, after the LoadModule instruction for geoip (which installation steps above will add automatically) add the following instruction:

<IfModule mod_geoip.c>
   GeoIPEnable On
   GeoIPScanProxyHeaders On
   GeoIPDBFile /usr/local/share/geoip/GeoIP.dat
   GeoIPDBFile /usr/local/share/geoip/GeoLiteCity.dat
</IfModule>

Now, let’s download the binary database (.dat) files indicated in the config in those locations:

$ cd /usr/local/share
$ mkdir geoip
$ cd geoip
$ wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCountry/GeoIP.dat.gz
$ gunzip GeoIP.dat.gz
$ wget http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
$ gunzip GeoLiteCity.dat.gz 

Once you are done setting up, restart Apache and configure a test script to inspect the contents of the new $_SERVER variable, you should see output like:

Array
(
    [GEOIP_ADDR] => #CENSORED#
    [GEOIP_CONTINENT_CODE] => NA
    [GEOIP_COUNTRY_CODE] => US
    [GEOIP_COUNTRY_NAME] => United States
    [GEOIP_REGION] => VA
    [GEOIP_REGION_NAME] => Virginia
    [GEOIP_CITY] => #CENSORED#
    [GEOIP_DMA_CODE] => #CENSORED#
    [GEOIP_METRO_CODE] => #CENSORED#
    [GEOIP_AREA_CODE] => #CENSORED#
    [GEOIP_LATITUDE] => #CENSORED#
    [GEOIP_LONGITUDE] => #CENSORED#
    [GEOIP_POSTAL_CODE] => #CENSORED#
)    

I put “#CENSORED#” for some values, but obviously there should be actual values present that you can use in your PHP app.

I believe that there’re instructions on the web for installing and configuring similar setup for Nginx and Light HTTPD as well. Approach should be very similar.

Enjoy.