NGINX Documentation

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. For NGINX Plus:

    • Install the GeoIP dynamic module for NGINX Plus:

      $ apt-get install nginx-plus-module-geoip
      
    • Enable the GeoIP dynamic module in the NGINX Plus configuration file with the load_module directive specified on the main configuration level:

      load_module modules/ngx_http_geoip_module.so;
      load_module modules/ngx_stream_geoip_module.so;
      

    For open source NGINX:

  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 the paths to the databases to the NGINX configuration with the geoip_country and geoip_city directives for HTTP, TCP/UDP, or both:

    http {
        #...
        geoip_country GeoIP/GeoIP.dat;
        geoip_city    GeoIP/GeoLiteCity.dat;
        #...
     }
       
    stream {
        #...
        geoip_country GeoIP/GeoIP.dat;
        geoip_city    GeoIP/GeoLiteCity.dat;
        #...
    }
    
  4. Use the standard variables from the GeoIP module (HTTP or TCP/UDP) to pass data to the map or split_clients directive.

    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;
    }
    #...
    

    If 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 in both the 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.