Close modal

Blog Post

Redirect traffic geographically with NGINX & geolocation

Development
Mon 28 November 2016
0 Comments


Sometimes you care about the region or country users come from to present them with a different experience, perhaps due to language or currency for a shop. Let's explore how to do that in a relatively easy few steps.

The setup

You've got some form of linux or bsd running nginx - let's assume it's Debian or Ubuntu.

First Steps

Install the geo-ip tools and update the database.

sudo apt-get update
sudo apt-get install nginx-full geoip-database
curl http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz | gunzip > /usr/share/GeoIP/GeoLiteCity.dat

The information is available as many formats: country or even city, however those are respectively long and impossible to list here. Also bare in mind the ip-based geolocation is an approximation that is rough but mostly adequate, you can usually rely on the country information if a user isn't using a proxy, the state and city information can vary especially with mobile networks. Let's just stick to continents here - below are the ones defined in the specification.

Code Continent Code Continent
AF Africa AN Antarctica
AS Asia EU Europe
AS North america OC Oceania
SA South america
Enter nginx

First of all, edit your nginx.conf in /etc/nginx/nginx/ or whereever you keep it, and add the following geoip_city definition as per below under http section.

http {
        # Tell Nginx where we keep our GeoLocation database
        geoip_city /usr/share/GeoIP/GeoLiteCity.dat;
        # all the rest...
        }

Using a very clever nginx directive called map which you can read all about here, we define a variable essentially based on a lookup table (which in this case uses the $geoip_city_continent_code to map the resultant value into $closest_server).

map $geoip_city_continent_code $closest_server {
        default international-waters.mybrand.com;
        AF      africas.mybrand.com;
        AN      penguins.mybrand.com;
        AS      asia.mybrand.com;
        EU      europe.mybrand.com;
        NA      northamericas.mybrand.com;
        OC      oceania.mybrand.com;
        SA      southamericas.mybrand.com;
}

Or to see this visually:

Now that'll give us a value we can use, but what do we do with it? Well there's a few options you could 301/302 (debatable which is more true as it hasn't been redirected either temporarily or permanently but anyway...), or you can use a rewrite rule to internally rewrite the url.

server {
    listen 127.0.0.1:80;
    server_name shop.mybrand.com;

    rewrite ^ $scheme://$closest_server$request_uri break;
}

server {
    listen 127.0.0.1:80;
    server_name africas.mybrand.com penguins.mybrand.com asia.mybrand.com europe.mybrand.com northamericas.mybrand.com oceania.mybrand.com southamericas.mybrand.com;
    root /srv/shop.mybrand.com/html;
    index index.html;
}

Conclusion

It's actually surprisingly simple to do this, and takes the heavy-lifting out of your code, which is a huge benefit for portability, and allows a much more flexible setup. This is just the tip of the iceberg as their are other parameters available such as country, state and region (as discussed). A word to the wise, if you're wanting to do this for language reasons, try using the user's locale (it'll be much more accurate and specific).