How to GeoIP block certain countries in Nginx with MaxMind

I spent too long thinking that ModSecurity was necessary for this, but you can do it with just the ngx_http_geoip2_module

Install nginx along with the module:

apt-get install nginx libnginx-mod-http-geoip2

Download the free GeoLite mmdb database from MaxMind into /etc/nginx/GeoLite2-Country.mmdb (you can and probably should use their tools to update this automatically).

Create /etc/nginx/conf.d/geoip.conf with the countries you want to block:

geoip2 /etc/nginx/GeoLite2-Country.mmdb {
        auto_reload 5m;
        $geoip2_metadata_country_build metadata build_epoch;
        $geoip2_data_country_code default=XX source=$remote_addr country iso_code;
}

map $geoip2_data_country_code $block_country {
        default 0;
        CN 1;    # China
        RU 1;    # Russia
        IR 1;    # Iran
        KP 1;    # North Korea
        XX 1;    # Unknown
}

Then in the server block simply block based on that variable:

server_name _;
    location / {
        if ($block_country) {
            return 403;
        }
        try_files $uri $uri/ =404;
    }

If you want to check if the country code is working before returning 403, you can add the country code to the logs by modifying nginx.conf:

log_format geoip_combined '$remote_addr - $remote_user [$time_local] '
                          '"$request" $status $body_bytes_sent '
                          '"$http_referer" "$http_user_agent" '
                          '$geoip2_data_country_code';
access_log /var/log/nginx/access.log geoip_combined;