// Tutorial //

How To Use Nginx as a Global Traffic Director on Debian or Ubuntu

Published on May 30, 2014
Default avatar
By Alex Soban
Developer and author at DigitalOcean.
How To Use Nginx as a Global Traffic Director on Debian or Ubuntu

Introduction

As your customer base grows, so does the distance between your server(s) and your customers. We all know that if your server load increases – you scale. But what to do when distance is the problem?

The solution is simple: install server(s) in geographical locations closer to your customer base and direct them based on their location. But how do we do this easily while being cost effective?

In this guide, we’ll configure Nginx to detect and redirect customers to a subdomain that points to a more geographically appropriate server.

Prerequisites

To complete this guide, you will need a user with sudo privileges. You also need to know how to create servers in different regions.

Assumptions

This article makes a few assumptions for ease of readability.

  • Your domain is www.example.com
  • Your primary server is in the US
  • You want to install a GTD for Europe and Asia
  • Your server IPs are as follows: US: 1.1.1.1 EU: 1.1.1.2 AS: 1.1.1.3

Subdomains and DNS configuration

Choosing subdomains is all up to you. For this tutorial, let’s use eu.example.com for Europe and as.example.com for Asia.

For each of those subdomains, add an A record in your DNS configuration with the IP of the server for that region:

  • eu.example.com - 1.1.1.2
  • as.example.com - 1.1.1.3

It should look something like this:

dns

Install Nginx and GeoIP

To have Nginx with a GeoIP module, you have two options: 1) use a precompiled package (only -full and -extra have GeoIP module) or 2) compile your nginx with the --with-http_geoip_module configuration parameter – In this case, you also need geoip-dev libraries.

Let’s use the already available repository packages.

sudo apt-get update
sudo apt-get install nginx-full geoip-database

Now both Nginx and GeoIP binaries are available. But there is one more thing: GeoIP’s city database includes region information. You need to download and install it manually.

wget -N http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
gunzip GeoLiteCity.dat.gz
mv GeoLiteCity.dat /usr/share/GeoIP/

Configure Nginx and your virtual host

Here we are going to tell Nginx where the GeoIP database files are. And in our virtual host, we are going to configure how Nginx will respond to requests based on their geoip information.

Open nginx.conf (default /etc/nginx/nginx.conf) with your preferred editor. Add the line geoip_city /usr/share/GeoIP/GeoLiteCity.dat;. Your nginx.conf will look like this:

http {
  geoip_city /usr/share/GeoIP/GeoLiteCity.dat;
  ...
}

Save it.

Now let’s edit your virtual host (default /etc/nginx/sites-available/default). Inside we need to create a map and add the subdomains to the server_name directive.

The map in Nginx allows us to set a variable $closest_server based on the value of $geoip_city_continent_code. You can read more about the Map module on Nginx’s documentation.

map $geoip_city_continent_code $closest_server {
  default www.example.com;
  EU      eu.example.com;
  AS      as.example.com;
}

Next we add the location-based subdomains to the $server_name directive:

server {
  server_name example.com
              www.example.com
              eu.example.com
              as.example.com;
  ...
}

The last part of the process is to make a condition in your virtual host that redirects visitors to the closest server. Add the following condition to your configuration:

server {
  ...

  if ($closest_server != $host) {
    rewrite ^ $scheme://$closest_server$request_uri break;
  }

  ...
}

After you’re done with all the changes your virtual host file would look like this:

map $geoip_city_continent_code $closest_server {
  default www.example.com;
  EU      eu.example.com;
  AS      as.example.com;
}

server {
  server_name example.com
              www.example.com
              eu.example.com
              as.example.com;

  if ($closest_server != $host) {
    rewrite ^ $scheme://$closest_server$request_uri break;
  }

  ...
}

Repeat this step for each server you want to configure. That way all of your servers will act as a traffic directors.

Run a few tests

After completing all of these steps, the final one is to test what you have done. When working with Nginx, always test new configurations before applying.

Nginx provides an option to test its configuration files, without affecting currently running Nginx. You can do that by running one of these commands:

nginx -t, service nginx configtest or /etc/init.d/nginx configtest

If everything is good - Reload your Nginx configuration:

nginx -s reload, service nginx reload or /etc/init.d/nginx reload

To see your traffic director in action. Open a browser and visit www.example.com:

If you visit the site using a proxy in Europe, you should be redirected to eu.example.com:

If you visit the site using a proxy in Asia, you should be redirected to as.example.com:

And from now on your global visitors will be immediately redirected to a server closest to them, improving their experience on your website.

<div class=“author”>Submitted by: <a href=“https://github.com/SobanVuex”>Alex Soban</a></div>

If you’ve enjoyed this tutorial and our broader community, consider checking out our DigitalOcean products which can also help you achieve your development goals.

Learn more here


About the authors
Default avatar
Developer and author at DigitalOcean.

Still looking for an answer?

Was this helpful?
10 Comments

This textbox defaults to using Markdown to format your answer.

You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!

Would it be possible to have it without the eu.xxx.com and just have everything on www.xxx.com. This would avoid SEO errors et cetera.

Configure Nginx and your virtual host

Add both of these lines in the /etc/nginx/nginx.conf file:

geoip_country /usr/share/GeoIP/GeoIP.dat;
geoip_city /usr/share/GeoIP/GeoLiteCity.dat;

Then in the /etc/nginx/fastcgi_params add at the end:

fastcgiparam REDIRECTSTATUS 200;
fastcgiparam GEOIPADDR $remoteaddr;
fastcgiparam GEOIPCOUNTRYCODE $geoipcountrycode;
fastcgiparam GEOIPCOUNTRYNAME $geoipcountryname;

fastcgiparam GEOIPREGION $geoipregion;
fastcgiparam GEOIPREGIONNAME $geoipregionname;
fastcgiparam GEOIPCITY $geoipcity;
fastcgiparam GEOIPAREACODE $geoipareacode;
fastcgiparam GEOIPLATITUDE $geoiplatitude;
fastcgiparam GEOIPLONGITUDE $geoiplongitude;
fastcgiparam GEOIPPOSTALCODE $geoippostal_code;

Add these in your /etc/nginx/sites-availables/default OR your config file

map $geoip_city_country_code $closest_server {
  default www.example.com;
  DE      www.example.ch;
  CH      www.example.ch;
  AT      www.example.ch;
  LI      www.example.ch;
}


	server_name example.com
              www.example.com
              example.ch
              www.example.ch;

  if ($closest_server != $host) {
    rewrite ^ $scheme://$closest_server$request_uri break;
  }

this is working fine for me…

Regards

I have added the redirection it’s working fine. Asian user can’t see the EU version. I have 2 domains example.ch FOR EU Peoples & example.com FOR Other Peoples

I am in Asia and i want to access .ch But it’s redirecting me to .com. What should i do If i want to add option. Asian users can also see .ch and EU users can also see .com version. with one click and bottom (See EU or** ASIA** Version) without proxy.

Thanks

@julesmoretti good job! Thanks for sharing!

And with multisite handling:

map $geoip_city_continent_code $closest_server {

  AF      eu.domain.com;  #Africa
  AS      as.domain.com;  #Asia
  EU      eu.domain.com;  #Europe
  NA      us.domain.com;  #North America
  OC      as.domain.com;  #Oceania
  SA      us.domain.com;  #South America
}

# retoute http://domain.com and http://www.domain.com to http://us.domain.com
server {
  listen 80;
  listen [::]:80;

  server_name domain.com
              www.domain.com;

  return 301 http://us.domain.com$request_uri;
}

# handles
server {
  listen 80;
  listen [::]:80;

  root /var/www/domain.com/html;
  index index.php index.html index.htm;

  # Make site accessible from http://localhost/
  server_name  us.domain.com;

  # firs part of dual condition check.
  # checks in the incoming host (us.domain.com) is not equal to $closest_server depending on contry code
  if ($closest_server != $host) {
    set $test  A;
  }

  #second part of dual condition checks that the incoming host us.domain.com is the same as the server_name so that there is no messing around with other domain names
  if ($host ~* $server_name) {
    set $test  "${test}B";
  }

  # checks that the above 2 conditions are met and if so redirect to other global server with appropriate subdomain eu.domain.com or whatever you set up
  if ($test = AB) {
    rewrite ^ $scheme://$closest_server$request_uri break;
  }

  # otherwise you are at the closest server and so serve the data
  location / {
    try_files $uri $uri/ =404;
  }

Next step is to synchronizing files and databases across those servers ;)

I think I followed your instructions to the letter and it does not work… Or I am unable to getting it to work… Configured the DNS appropriately to point to the servers. Page loads at the specific locations. But when I add to my test.php page some code like this:

 <?php
     if (getenv(HTTP_X_FORWARDED_FOR)) {
        $pipaddress = getenv(HTTP_X_FORWARDED_FOR);
        $ipaddress = getenv(REMOTE_ADDR);
        echo "Your Proxy IP address is : ".$pipaddress. " (via $ipaddress) " ;
    } else {
        $ipaddress = getenv(REMOTE_ADDR);
        echo "Your IP address is : $ipaddress";
    }
    $country = getenv(GEOIP_COUNTRY_NAME);
    $country_code = getenv(GEOIP_COUNTRY_CODE);
    echo "<br/>Your country : $country ( $country_code ) ";
?>

<?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>';
?>

All I get is:

Your IP address is : 74.62.192.83
Your country : ( ) country_code: 
country_code3: 
country_name: 
city_country_code: 
city_country_code3: 
city_country_name: 
region: 
city: 
postal_code: 
city_continent_code: 
latitude: 
longitude:

So it seems not to be working appropriately. I would love some update or clarification as this is what I need for a client. My configurations for each servers are smallest droplet size with Ubuntu and LAMP.

And this is the information I altered in the nginx virtual site conf of the default server which I want to be eu.mypersonaldomain.extension:

map $geoip_city_continent_code $closest_server {
  default mypersonaldomain.extension;
  US      us.mypersonaldomain.extension;
  AS      as.mypersonaldomain.extension;
#  EU      eu.mypersonaldomain.extension;
}

server {
        server_name     mypersonaldomain.extension
                        wwwmypersonaldomain.extension
                        us.mypersonaldomain.extension
                        as.mypersonaldomain.extension
                        eu.mypersonaldomain.extension;

        if ($closest_server != $host) {
          rewrite ^ $scheme://$closest_server$request_uri break;
        }

        listen 80 eu.mypersonaldomain.extension;
        listen [::]:80 mypersonaldomain.extension ipv6only=on;

        root /var/www/html;
        index index.html index.htm;

        # Make site accessible from http://localhost/
        eu.mypersonaldomain.extension localhost;

Also within the /etc/nginx/sites-available/ I renamed the config file to eu.mypersonaldomain.extension

and run:

ln -s /etc/nginx/sites-available/eu.mypersonaldomain.extension /etc/nginx/sites-enabled/eu.mypersonaldomain.extension

And of course restarted a bunch of times… too

Thank you for your help…

What would be the way to redirect to a subdirectory for a specific language, like /en/ , /fr/ …?

Great post!

Just would like to left a quick note: if you are downloading the GeoIP database from http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz, you don’t need to install the geoip-database package via apt-get too.

Great post!

Just would like to put this quick note: if you are downloading the GeoIP database from http://geolite.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz, you don’t need to install the geoip-database package via apt-get too.

its dose not work for me . i dont know why … did all the steps but nothing i keep having errors