Another very powerful piece of information you can retrieve is the geographical location of the client.
Although there are APIs you can call using vmod_http, the overhead
may slow us down at scale. A superior solution is to use MaxMind’s
geoIP database. This proprietary database, which has a free version,
maps IP addresses to geographical locations.
There are both open source VMODs and proprietary VMODs available.
They all rely on libmaxminddb.
As a developer, you can go to https://dev.maxmind.com/ to obtain a free version of the geoIP database:
GeoLite2-Country.mmdb: a database that only contains country and
continent informationGeoLite2-City.mmdb: an extended version of the database that
contains country, continent, city and geolocation informationAs an administrator, it is your responsibility to keep the database up-to-date.
Being able to geographically locate the user allows you to perform geotargeting, but even geoblocking.
Geotargeting involves putting the user in a certain category based on their location. This is important when you build your own CDN because you can send users to the closest point of presence (PoP). Having content as close to your users as possible will decrease latency and increase the quality of experience.
Another example of geotargeting is presenting localized content to the user. Many multinational corporations have separate websites per country. Being able to suggest the right one based on the client IP address contributes to a good user experience.
Geoblocking is used to refuse access to certain content. Websites or OTT video platforms that are funded with taxpayer money will refuse access to their platforms for users outside of the country.
vmod_geoip2 is an open source VMOD that is available on
https://github.com/fgsch/libvmod-geoip2.
The geoip2.geoip2() function loads the MaxMind GeoIP database and
returns an object. This object uses the .lookup() method to retrieve
geographical information.
Here’s a very simple geoblocking example:
vcl 4.1;
import geoip2;
sub vcl_init {
	new country = geoip2.geoip2("/etc/varnish/GeoLite2-Country.mmdb");
}
sub vcl_recv {
	if(country.lookup("country/iso_code", client.ip) != "BE") {
		return(403,"Access from " + country.lookup("country/names/en", client.ip) + " not allowed");
	}
}
vmod_mmdb is a Varnish Enterprise module that uses the same database
file provided by MaxMind. Here’s the equivalent of the previous
example:
vcl 4.1;
import mmdb;
sub vcl_init {
	new country = mmdb.init("/etc/varnish/GeoLite2-Country.mmdb");
}
sub vcl_recv {
	// there is a convenience function to
	// retrieve the country code directly, let's use it!
	if(country.country_code(client.ip) != "BE") {
		return(403,"Access from " + country.lookup(client.ip, "country/names/en") + " not allowed");
	}
}
Both VMODs have a .lookup() method that takes a lookup path. This
path is used to retrieve the information from the database.
The GeoLite2-Country.mmdb database only contains country and continent
information. Here are a couple of examples of various paths:
continent/code: for example EUcontinent/names/en: for example Europecountry/is_in_european_union: for example truecountry/names/iso_code: for example BEcountry/names/en: for example BelgiumThere is a German, Spanish, French, Japanese, Portuguese, Russian and
Chinese alternative for continent/names/en and country/names/en.
Here’s an overview of paths you can use to retrieve the country name in the various supported languages:
country/names/decountry/names/encountry/names/escountry/names/frcountry/names/jacountry/names/pt-BRcountry/names/rucountry/names/zh-CNThese language codes are also available for continent names.
The GeoLite2-City.mmdb also contains all of the above but is
supplemented with city and geolocation information.
Here’s an overview of additional lookup filters for the city database:
city/names/decity/names/encity/names/escity/names/frcity/names/jacity/names/pt-BRcity/names/rucity/names/zh-CNlocation/accuracy_radiuslocation/latitudelocation/longitudelocation/time_zonepostal/codeThere is also a
subdivisionfield that describes states, provinces, or communities within a country, but we’re not going to cover this in the book.
The following example looks up the continent code for the client IP
address based on the GeoLite2-Country.mmdb databases, and matches this
with available backends stored using vmod_kvstore.
If no corresponding backend is found, the default one is used.
vcl 4.1;
import kvstore;
import mmdb;
import std;
backend default {
    .host="default.backend.example.com";
}
backend de {
    .host="de.backend.example.com";
}
backend us {
    .host="us.backend.example.com";
}
backend br {
    .host="br.backend.example.com";
}
backend jp {
    .host="jp.backend.example.com";
}
backend sa {
    .host="sa.backend.example.com";
}
backend au {
    .host="au.backend.example.com";
}
sub vcl_init {
    new geo = mmdb.init("/etc/varnish/GeoLite2-Country.mmdb");
    new backends = kvstore.init();
    backends.set_backend("EU",de);
    backends.set_backend("NA",us);
    backends.set_backend("SA",br);
    backends.set_backend("AS",jp);
    backends.set_backend("OC",au);
    backends.set_backend("AF",sa);
}
sub vcl_recv {
    set req.backend_hint = backends.get_backend(
        geo.lookup(
            client.ip,
            "continent/code"
        ),
        default
    );
}
The example above features backends in Germany, the United States, Brazil, Japan, Australia and South Africa. Each of these endpoints is associated with the corresponding continent code.
If you’re in Antarctica, or there is an issue mapping the IP address to a location, the default backend is used.
This same example can easily be reproduced using the open source
vmod_ geoip2.