Can I use mod_geoip to determine the country of a visitor to my site?

We have the Apache mod_geoip module available on our servers, and it can be used to determine the country of each visitor.

On this page:

Enabling mod_geoip

To enable it for your site, add these lines to your .htaccess file:

<IfModule mod_geoip.c>
  GeoIPEnable On
</IfModule>

When it’s enabled, the Apache server attempts to make several extra environment variables available, including:

GEOIP_ADDR
GEOIP_CONTINENT_CODE
GEOIP_CONTINENT_CODE_V6
GEOIP_COUNTRY_CODE
GEOIP_COUNTRY_CODE_V6
GEOIP_COUNTRY_NAME
GEOIP_COUNTRY_NAME_V6
GEOIP_REGION
GEOIP_REGION_V6
GEOIP_REGION_NAME
GEOIP_REGION_NAME_V6
GEOIP_CITY
GEOIP_CITY_V6

These are documented on the mod_geoip reference page.

Note that for IPv6 connections, the variable has a different name ending in _V6, so you need to check both unless you disable IPv6 for your website (which we don’t recommend).

What can I do with this?

The most common thing to do is block certain countries or continents. For example, adding these lines to your site’s .htaccess file will attempt to block all requests that originate in China or Russia (but see “Limitations” below):

<IfModule mod_geoip.c>
  GeoIPEnable On
</IfModule>
SetEnvIf GEOIP_COUNTRY_CODE CN BlockCountry
SetEnvIf GEOIP_COUNTRY_CODE_V6 CN BlockCountry
SetEnvIf GEOIP_COUNTRY_CODE RU BlockCountry
SetEnvIf GEOIP_COUNTRY_CODE_V6 RU BlockCountry
Deny from env=BlockCountry

This example will try to block everyone outside North America:

<IfModule mod_geoip.c>
  GeoIPEnable On
</IfModule>
SetEnvIf GEOIP_CONTINENT_CODE AF BlockContinent # Block Africa
SetEnvIf GEOIP_CONTINENT_CODE AS BlockContinent # Block Asia
SetEnvIf GEOIP_CONTINENT_CODE EU BlockContinent # Block Europe
SetEnvIf GEOIP_CONTINENT_CODE OC BlockContinent # Block Oceania
SetEnvIf GEOIP_CONTINENT_CODE SA BlockContinent # Block South America
SetEnvIf GEOIP_CONTINENT_CODE_V6 AF BlockContinent # Block Africa
SetEnvIf GEOIP_CONTINENT_CODE_V6 AS BlockContinent # Block Asia
SetEnvIf GEOIP_CONTINENT_CODE_V6 EU BlockContinent # Block Europe
SetEnvIf GEOIP_CONTINENT_CODE_V6 OC BlockContinent # Block Oceania
SetEnvIf GEOIP_CONTINENT_CODE_V6 SA BlockContinent # Block South America
Deny from env=BlockContinent

Once you’ve added GeoIPEnable On to your .htaccess file, you can also access these variables from PHP scripts, too, like this:

<?php
print "You are visiting from ";
if (isset($_SERVER['GEOIP_COUNTRY_NAME_V6']))
    print $_SERVER['GEOIP_COUNTRY_NAME_V6'];
else
    print $_SERVER['GEOIP_COUNTRY_NAME'];

Or this:

<?php
if ($_SERVER['GEOIP_COUNTRY_CODE'] === 'RU'
    || $_SERVER['GEOIP_COUNTRY_CODE_V6'] === 'RU')
{
    print "Visitors from Russia are blocked.";
    exit();
}

Limitations

The geographical mapping of IP addresses to countries and cities is not perfect. For example:

  • Information about new IP addresses or changes may not yet be available.
  • If a multinational company has employees in different countries, they may all be shown as coming from the country of the company headquarters.
  • Visitors may be using a VPN that shows them in one country when they’re really in another.
  • The mapping of IP addresses may have occasional errors.

The database this system uses is provided by a third party company called MaxMind, and we don’t control the data. They estimate it’s “99.8% accurate on a country level, 90% accurate on a state level in the US, and 81% accurate for cities in the US within a 50 kilometer radius”.

Because of this, you should not rely on it for anything critical. For example, if you use it to try to block everyone outside of the US from viewing your site, you’ll probably block about 1 out of every 500 real US visitors. It’s better to use it for things that aren’t mission-critical, like blocking certain visitors from easily accessing comment forms.

Advanced topic: Mixing mod_geoip and mod_rewrite

If you’re a technical expert comfortable with Apache’s mod_rewrite, you can mix the two in useful ways. The lines below block all HTTP/1.0 POST requests from Russia or China, for example, without affecting any HTTP/1.1 requests, or any HTTP/1.0 requests from other sources:

<IfModule mod_geoip.c>
  GeoIPEnable On
  SetEnvIf GEOIP_COUNTRY_CODE RU BlockCountry
  SetEnvIf GEOIP_COUNTRY_CODE_V6 RU BlockCountry
  SetEnvIf GEOIP_COUNTRY_CODE CN BlockCountry
  SetEnvIf GEOIP_COUNTRY_CODE_V6 CN BlockCountry
  
  RewriteEngine On
  RewriteCond %{ENV:BlockCountry} 1
  RewriteCond %{THE_REQUEST} ^POST.+HTTP/1.0$ [nocase]
  RewriteRule .* - [forbidden,last]
</IfModule>