There is a new version of this tutorial available for Ubuntu 16.04 (Xenial Xerus).

Using GeoIP With Nginx On Debian Squeeze/Ubuntu 11.04

Version 1.0
Author: Falko Timme
Follow me on Twitter

This tutorial explains how to use the GeoIP module with nginx on Debian Squeeze/Ubuntu 11.04 to find out where your visitors come from. The GeoIP module sets multiple variables like $geoip_country_name, $geoip_country_code, $geoip_city, etc. that you can use in your PHP scripts or directly in your nginx configuration, for example to serve content in different languages based on the user's country.

I do not issue any guarantee that this will work for you!

 

1 Preliminary Note

I'm using the website www.example.com here with the document root /var/www/www.example.com/web/ and the Nginx vhost configuration file /etc/nginx/sites-enabled/www.example.com.vhost.

A note for Ubuntu users:

Because we must run all the steps from this tutorial with root privileges, we can either prepend all commands in this tutorial with the string sudo, or we become root right now by typing

sudo su

 

2 Find Out If Nginx Has Support For GeoIP

Before we start we must find out if the GeoIP module is built into our nginx server:

nginx -V
root@server1:~# nginx -V
nginx version: nginx/0.8.54
TLS SNI support enabled
configure arguments: --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-log-path=/var/log/nginx/access.log --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --lock-path=/var/lock/nginx.lock --pid-path=/var/run/nginx.pid --with-debug --with-http_addition_module --with-http_dav_module --with-http_geoip_module --with-http_gzip_static_module --with-http_image_filter_module --with-http_realip_module --with-http_stub_status_module --with-http_ssl_module --with-http_sub_module --with-http_xslt_module --with-ipv6 --with-sha1=/usr/include/openssl --with-md5=/usr/include/openssl --with-mail --with-mail_ssl_module --add-module=/build/buildd/nginx-0.8.54/debian/modules/nginx-upstream-fair
root@server1:~#

 

3 Download The GeoIP Databases

On Debian and Ubuntu, there is the package geoip-database that can be installed through apt, but it is a bit outdated and only contains GeoIP.dat (country database), not GeoLiteCity.dat (city database). Therefore we don't install that package, but download fresh copies from the GeoIP web site to the /etc/nginx/geoip directory:

mkdir /etc/nginx/geoip
cd /etc/nginx/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

 

4 Configure Nginx

Now we configure nginx. Open /etc/nginx/nginx.conf...

vi /etc/nginx/nginx.conf

... and add the geoip_country and geoip_city directives to the http {} container:

[...]
http {
        geoip_country  /etc/nginx/geoip/GeoIP.dat; # the country IP database
        geoip_city     /etc/nginx/geoip/GeoLiteCity.dat; # the city IP database
[...]

The geoip_country directive makes the following variables available:

  • $geoip_country_code - two-letter country code, for example, RU, US.
  • $geoip_country_code3 - three-letter country code, for example, RUS, USA.
  • $geoip_country_name - the (verbose) name of the country, for example, Russian Federation, United States, etc.

The geoip_city directive provides the following variables:

  • $geoip_city_country_code - two-letter country code, for example, RU, US.
  • $geoip_city_country_code3 - three-letter country code, for example, RUS, USA.
  • $geoip_city_country_name - the name of the country, for example, Russian Federation, United States - if available.
  • $geoip_region - the name of region (province, region, state, province, federal land, and the like), for example, Moscow City, DC - if available.
  • $geoip_city - the name of the city, for example, Moscow, Washington, Lisbon, etc. - if available.
  • $geoip_postal_code - zip code or postal code - if available.
  • $geoip_city_continent_code - if available.
  • $geoip_latitude - latitude - if available.
  • $geoip_longitude - longitude - if available.

In order to make these variables available to your PHP scripts as well, we must set a few fastcgi_param directives. It is best to do this in the file /etc/nginx/fastcgi_params where the other fastcgi_param directives are:

vi /etc/nginx/fastcgi_params
[...]
### SET GEOIP Variables ###
fastcgi_param GEOIP_COUNTRY_CODE $geoip_country_code;
fastcgi_param GEOIP_COUNTRY_CODE3 $geoip_country_code3;
fastcgi_param GEOIP_COUNTRY_NAME $geoip_country_name;
fastcgi_param GEOIP_CITY_COUNTRY_CODE $geoip_city_country_code;
fastcgi_param GEOIP_CITY_COUNTRY_CODE3 $geoip_city_country_code3;
fastcgi_param GEOIP_CITY_COUNTRY_NAME $geoip_city_country_name;
fastcgi_param GEOIP_REGION $geoip_region;
fastcgi_param GEOIP_CITY $geoip_city;
fastcgi_param GEOIP_POSTAL_CODE $geoip_postal_code;
fastcgi_param GEOIP_CITY_CONTINENT_CODE $geoip_city_continent_code;
fastcgi_param GEOIP_LATITUDE $geoip_latitude;
fastcgi_param GEOIP_LONGITUDE $geoip_longitude;

(Make sure you have the line include /etc/nginx/fastcgi_params; in your location ~ \.php$ {} container in your vhost configuration, because otherwise the above configuration is useless for your vhost.)

If you use nginx as a reverse proxy and want to pass the GeoIP variables to the backend, you should create/edit the file /etc/nginx/proxy.conf...

vi /etc/nginx/proxy.conf

... and add the following lines to it:

[...]
### SET GEOIP Variables ###
proxy_set_header GEOIP_COUNTRY_CODE $geoip_country_code;
proxy_set_header GEOIP_COUNTRY_CODE3 $geoip_country_code3;
proxy_set_header GEOIP_COUNTRY_NAME $geoip_country_name;
proxy_set_header GEOIP_CITY_COUNTRY_CODE $geoip_city_country_code;
proxy_set_header GEOIP_CITY_COUNTRY_CODE3 $geoip_city_country_code3;
proxy_set_header GEOIP_CITY_COUNTRY_NAME $geoip_city_country_name;
proxy_set_header GEOIP_REGION $geoip_region;
proxy_set_header GEOIP_CITY $geoip_city;
proxy_set_header GEOIP_POSTAL_CODE $geoip_postal_code;
proxy_set_header GEOIP_CITY_CONTINENT_CODE $geoip_city_continent_code;
proxy_set_header GEOIP_LATITUDE $geoip_latitude;
proxy_set_header GEOIP_LONGITUDE $geoip_longitude;

(Make sure you use the line include /etc/nginx/proxy.conf; in your nginx proxy configuration because otherwise the backend cannot use the GeoIP variables.)

Now reload nginx...

/etc/init.d/nginx reload

... for the changes to take effect.

If you use PHP-FPM as your FastCGI daemon (like in Installing Nginx With PHP5 (And PHP-FPM) And MySQL Support On Ubuntu 11.04), restart it as follows:

/etc/init.d/php5-fpm restart

If you use lighttpd's spawn-fcgi program as your FastCGI daemon (like in Installing Nginx With PHP5 And MySQL Support On Debian Squeeze), we must kill the current spawn-fcgi process (running on port 9000) and create a new one. Run

netstat -tap

to find out the PID of the current spawn-fcgi process:

root@server1:~# netstat -tap
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address           Foreign Address         State       PID/Program name
tcp        0      0 *:sunrpc                *:*                     LISTEN      734/portmap
tcp        0      0 *:www                   *:*                     LISTEN      2987/nginx
tcp        0      0 *:ssh                   *:*                     LISTEN      1531/sshd
tcp        0      0 *:57174                 *:*                     LISTEN      748/rpc.statd
tcp        0      0 localhost.localdom:smtp *:*                     LISTEN      1507/exim4
tcp        0      0 localhost.localdom:9000 *:*                     LISTEN      1542/php5-cgi
tcp        0      0 localhost.localdo:mysql *:*                     LISTEN      1168/mysqld
tcp        0     52 server1.example.com:ssh 192.168.0.198:2462      ESTABLISHED 1557/0
tcp6       0      0 [::]:www                [::]:*                  LISTEN      2987/nginx
tcp6       0      0 [::]:ssh                [::]:*                  LISTEN      1531/sshd
tcp6       0      0 ip6-localhost:smtp      [::]:*                  LISTEN      1507/exim4
root@server1:~#

In the above output, the PID is 1542, so we can kill the current process as follows:

kill -9 1542

Afterwards we create a new spawn-fcgi process:

/usr/bin/spawn-fcgi -a 127.0.0.1 -p 9000 -u www-data -g www-data -f /usr/bin/php5-cgi -P /var/run/fastcgi-php.pid

 

5 A Short Test

To see if the GeoIP module is working correctly, we can create a small PHP file in our www.example.com web space (e.g. /var/www/www.example.com/web):

vi /var/www/www.example.com/web/geoiptest.php

We can access the GeoIP variables as follows:

$geoip_country_code = getenv(GEOIP_COUNTRY_CODE);

Or like this:

$geoip_country_code = $_SERVER['GEOIP_COUNTRY_CODE'];
<html>
<body>
<?php
$geoip_country_code = getenv(GEOIP_COUNTRY_CODE);
/*
$geoip_country_code = $_SERVER['GEOIP_COUNTRY_CODE']; // works as well
*/
$geoip_country_code3 = getenv(GEOIP_COUNTRY_CODE3);
$geoip_country_name = getenv(GEOIP_COUNTRY_NAME);
$geoip_city_country_code = getenv(GEOIP_CITY_COUNTRY_CODE);
$geoip_city_country_code3 = getenv(GEOIP_CITY_COUNTRY_CODE3);
$geoip_city_country_name = getenv(GEOIP_CITY_COUNTRY_NAME);
$geoip_region = getenv(GEOIP_REGION);
$geoip_city = getenv(GEOIP_CITY);
$geoip_postal_code = getenv(GEOIP_POSTAL_CODE);
$geoip_city_continent_code = getenv(GEOIP_CITY_CONTINENT_CODE);
$geoip_latitude = getenv(GEOIP_LATITUDE);
$geoip_longitude = getenv(GEOIP_LONGITUDE);
echo 'country_code: '.$geoip_country_code.'<br>';
echo 'country_code3: '.$geoip_country_code3.'<br>';
echo 'country_name: '.$geoip_country_name.'<br>';
echo 'city_country_code: '.$geoip_city_country_code.'<br>';
echo 'city_country_code3: '.$geoip_city_country_code3.'<br>';
echo 'city_country_name: '.$geoip_city_country_name.'<br>';
echo 'region: '.$geoip_region.'<br>';
echo 'city: '.$geoip_city.'<br>';
echo 'postal_code: '.$geoip_postal_code.'<br>';
echo 'city_continent_code: '.$geoip_city_continent_code.'<br>';
echo 'latitude: '.$geoip_latitude.'<br>';
echo 'longitude: '.$geoip_longitude.'<br>';
?>
</body>
</html>

Call that file in a browser (http://www.example.com/geoiptest.php), and you should see GeoIP at work (make sure that you're calling the file from a public IP address, not a local one):

It is also possible to use GeoIP varibles directly in the nginx configuration, e.g. as follows:

vi /etc/nginx/sites-enabled/www.example.com.vhost
[...]
        location / {
            index index.html index.php;
            try_files /index_$geoip_country_code.html /index.html;
        }
[...]
/etc/init.d/nginx reload

In this example, if a visitor comes from Germany (country code: DE), and the file index_DE.html exists, then this file is served, otherwise the default index.html file is served:

This can be used to serve content in different languages, based on the user's origin.

 

 

About The Author

Falko Timme is the owner of Boost Your Site mit Timme Hosting - ultra-schnelles nginx-WebhostingTimme Hosting (ultra-fast nginx web hosting). He is the lead maintainer of HowtoForge (since 2005) and one of the core developers of ISPConfig (since 2000). He has also contributed to the O'Reilly book "Linux System Administration".

Share this page:

0 Comment(s)