NGINX

Upgrade to GeoIP2 with NGINX on CentOS/RHEL

by , , revisited on


We have by far the largest RPM repository with dynamic stable NGINX modules and VMODs for Varnish 4.1 and 6.0 LTS. If you want to install NGINX, Varnish, and lots of useful modules for them, this is your one-stop repository to get all performance-related software.
You have to maintain an active subscription in order to be able to use the repository!

GeoLite Legacy databases are discontinued as of January 2, 2019. They are not updated nor any longer available for download.
Every user should move to GeoLite2 databases, a more contemporary version of the GeoLite Legacy geolocation databases which are still available in a free version updated every month.

GeoIP2 NGINX module can be installed from GetPageSpeed repository for CentOS/RHEL (6, 7, 8) to easily migrate from GeoIP to GeoIP2.

By using nginx-module-geoip2 package, only a couple of lines in the NGINX configuration file are needed to configure the module.

GeoIP2 module installation

If you haven’t used our repository in the past, add up the release package for it first.

sudo yum -y install https://extras.getpagespeed.com/release-latest.rpm

Now, to install NGINX and the GeoIP2 module, you would run:

sudo yum install nginx nginx-module-geoip2

Then add the following at the top of your /etc/nginx/nginx.conf:

load_module modules/ngx_http_geoip2_module.so;

If your NGINX was running, reload it to pick up on the newly added module via service nginx reload.
Otherwise, run NGINX service via service nginx start.

GeoIP2 configuration

First, you need to get a hold of the latest MaxMind GeoLite2 country/city free databases.
Life is so easy when you use our repository, eh? We provide the geoipupdate program that is capable of fetching those databases.

Run:

sudo yum install geoipupdate geoipupdate-cron

Then:

  • Sign up for a MaxMind account (no purchase required)
  • Set your password and create a license key
  • Configure /etc/GeoIP.conf following instructions here, or simply use this template:
AccountID YOUR_ACCOUNT_ID_HERE
LicenseKey YOUR_LICENSE_KEY_HERE
EditionIDs GeoLite2-Country GeoLite2-City

If you’re using commercial databases, you will adjust the EditionIDs appropriately.

Now you can fetch the databases by running geoipupdate without arguments, once.
The geoipupdate will download GeoLite2-City.mmdb and GeoLite2-Country.mmdb over to /usr/share/GeoIP/ directory.

The weekly cron that was installed will take care of continuous updates of database files.

Tip: you can query IP geolocation using command line by installing libmaxminddb-devel package then running geolookups like this:

mmdblookup --file /usr/share/GeoIP/GeoLite2-Country.mmdb --ip 8.8.8.8 country names en

Now it’s time to tell NGINX about our GeoIP databases. In nginx.conf, preferably in the http { ... } section:

http {
    ...
    geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb {
        auto_reload 5m;
        $geoip2_metadata_country_build metadata build_epoch;
        # $geoip2_data_country_code default=US source=$variable_with_ip country iso_code;
        $geoip2_data_country_code default=US country iso_code;
        $geoip2_data_country_name country names en;
    }

    geoip2 /usr/share/GeoIP/GeoLite2-City.mmdb {
        $geoip2_data_city_name default=London city names en;
    }
    ....

    fastcgi_param COUNTRY_CODE $geoip2_data_country_code;
    fastcgi_param COUNTRY_NAME $geoip2_data_country_name;
    fastcgi_param CITY_NAME    $geoip2_data_city_name;
    ....
}

Testing GeoIP2 country and city fetching

The easiest way to confirm that NGINX is fetching all GeoIP information is by using a temporary header on any server { }.
Using Headers-More module to avoid common configuration pitfalls:

more_set_headers "X-Country: $geoip2_data_country_name";

If you’re not using Headers-More module already, stick to the standard directive:

add_header X-Country $geoip2_data_country_name;

Then check headers via curl -IL https://your.example.com/. This should yield, among other headers:

x-country: Your Country Name

Now that the GeoIP2 module is confirmed to work, we can review some use cases.

Whitelisting countries

One use case is whitelisting a website to specific countries. Only specific countries will be able to access the website.
In the following example, we restrict access to the website for all countries, except visitors from Canada and USA:

map $geoip2_data_country_code $allowed_country {
  default no;
  CA yes;
  US yes;
}

server {
    server_name example.com;
    if ($allowed_country = no) {
        return 403;
    }
    # ... 
}

Only visitors from Canada and the USA will be able to access the website. Others will be denied access.

Blacklisting countries

Instead of the whitelisting, you make want to take the blacklisting approach.
This is useful when you want the website to operate worldwide with a few exceptions.
For example, let’s allow access to the website for all countries, except Singapore:

map $geoip2_data_country_code $blacklisted_country {
  default no;
  SG yes;
}

server {
    server_name example.com;
    if ($blacklisted_country = yes) {
        return 403;
    }
    # ... 
}

Country-specific redirects

Suppose that you have one international website, but you also have a few country-specific:

  • example.com (international)
  • se.example.com for Sweden
  • ca.example.com for Canada

You want visitors from supported countries to be automatically redirected from the international website to country-specific websites.
This can be achieved with a configuration similar to the following:

map $geoip2_data_country_code $redirect_domain {
    default no;
    SE 'se.example.com';
    CA 'ca.example.com';
}

server {
    server example.com;
    if ($redirect_domain != no) {
        return 302 https://${redirect_domain}$request_uri;
    }
    # ....
}

This way, e.g. Sweden visitors who land at example.com will be redirected to se.example.com.
Canadian visitors will be redirected to ca.example.com.
Other visitors will not be redirected.

Say, a developer from India wants to work with each country-specific website. To exclude an IP from redirects, you can
leverage the geo directive and multiple maps:

geo $exclude_redirect_domain {    
    default no;    
    1.2.3.4 yes;
}
map $geoip2_data_country_code:$exclude_redirect_domain $redirect_domain {    
    default no;    
    SE:no 'se.example.com';    
    CA:no 'ca.example.com';
}

Display country ID in the access_log of NGINX

It can be useful for debugging or monitoring, to see the country code of each visitor in the access_log.
Simply add $geoip2_data_country_code in your log_format directive. E.g. place in nginx.conf:

log_format main
'$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for" '
'$request_time $upstream_response_time $pipe '
'$geoip2_data_country_code';

Country codes

Wondering what the two-letter country code is for a particular country?
Check the GeoNames country list.
The values in the first column are acceptable by the GeoIP2 module.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.