Restricting access by geographical location

Introduction

NGINX and NGINX Plus can differentiate users based on their geographical location. For example, you can have different website content for different countries, or you can restrict content distribution to a particular country or city.

NGINX uses third-party MaxMind databases to match the IP address of the user and its location. As soon as the geoposition is known, it is then possible to use geoip-based variables in the map or the split_clients module.

Restricting by geographical location works both for http and tcp/udp protocols.

Prerequisites

Configuring GeoIP in NGINX and NGINX Plus

  1. Make sure your NGINX Open Source is compiled with the --with-http_geoip_module and/or --with-stream_geoip_module configuration flag:

    $ nginx -V 2>&1 | grep -- 'http_geoip_module'
    $ nginx -V 2>&1 | grep -- 'stream_geoip_module'

    Skip this step for NGINX Plus as it already includes geoip modules for http and stream.

  2. Download and unzip legacy Geo Country and City databases from the MaxMind download page:

    $ 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
  3. Add paths to databases to NGINX configuration with http’s geoip_country and geoip_city, or stream’s geoip_country and geoip_city directives:

    http {
        ...
        geoip_country GeoIP/GeoIP.dat;
        geoip_city    GeoIP/GeoLiteCity.dat;
        ...
    }

    and/or:

    stream {
        ...
        geoip_country GeoIP/GeoIP.dat;
        geoip_city    GeoIP/GeoLiteCity.dat;
        ...
    }
  4. Use variables of geoip_country and geoip_city directives to pass data to map or split_clients module.

    For example, using the $geoip_city_continent_code variable of the geoip_city directive, and the map module, you can create another variable whose value will be the closest server basing on a continent location:

    ...
    map $geoip_city_continent_code $nearest_server {
        default default {};
        EU      eu;
        NA      na;
        AS      as;
        AF      af;
    ...

    Then you can choose an upstream server basing on the value passed in the $nearest_server variable:

    ...
    server {
        listen 12346;
        proxy_pass $nearest_server;
    }
    
     upstream eu {
        server eu1.example.com:12345;
        server eu2.example.com:12345;
    }
    
    upstream na {
        server na1.example.com:12345;
        server na2.example.com:12345;
    }
    ...

    It the continent is Europe, then the value of the $nearest_server will be eu, and the connection will be passed to the eu upstream via the proxy_pass directive.

Complete Example

This example can be applied to both http and stream contexts.

 # can be either "http {" or "stream {"
    ...
    geoip_country GeoIP/GeoIP.dat;
    geoip_city    GeoIP/GeoLiteCity.dat;

    map $geoip_city_continent_code $nearest_server {
        default default {};
        EU      eu;
        NA      na;
        AS      as;
        AF      af;

    server {
        listen 12346;
        proxy_pass $nearest_server;
    }

     upstream eu {
        server eu1.example.com:12345;
        server eu2.example.com:12345;
    }

    upstream na {
        server na1.example.com:12345;
        server na2.example.com:12345;
    }
}

In this example, the IP address will be checked in the GeoLiteCity.dat database, the result will be written to the $geoip_city_continent_code variable. NGINX will match the value of the variable against values in the map directive and white the result in the custom variable, in our example $nearest_server. Basing on the value of the $nearest_server, the proxy_pass directive will choose a corresponding upstream server.