Why Use GeoIP Restriction at the Edge

GeoIP restriction in Nginx helps reduce unwanted traffic before requests reach your applications. When combined with ModSecurity, you get two complementary protection layers:

  • GeoIP policy: block traffic from selected countries.
  • ModSecurity WAF: inspect and block malicious payloads and attack patterns.

This approach is useful when you want strict geographic access control and stronger edge filtering.

1. Install GeoIP2 Module and Country Database

On Ubuntu/Debian:

sudo apt update
sudo apt install -y libnginx-mod-http-geoip2

Place the GeoLite2 country database in a stable path:

sudo mkdir -p /usr/share/GeoIP
sudo cp GeoLite2-Country.mmdb /usr/share/GeoIP/GeoLite2-Country.mmdb

2. Global GeoIP Mapping in nginx.conf (Single Source of Truth)

Define GeoIP extraction and a country policy map once in the http block of /etc/nginx/nginx.conf:

geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb {
    $geoip2_data_country_iso_code country iso_code;
}

map $geoip2_data_country_iso_code $allowed_country {
    default yes;
    RU no; # Russia
    UA no; # Ukraine
    CN no; # China
    BY no; # Belarus
    IL no; # Israel
    CO no; # Colombia
    LT no; # Lithuania
    LV no; # Latvia
    EE no; # Estonia
}

This is the global policy definition. You manage blocked countries in one place.

3. Recommended Centralized Enforcement (Global Management)

Use a shared snippet and include it in every active server block.

Create /etc/nginx/snippets/geoip-country-block.conf:

if ($allowed_country = no) {
    return 444;
}

Then include it in each site server block:

server {
    server_name Site1.mouradcloud.com;

    include /etc/nginx/snippets/geoip-country-block.conf;

    modsecurity on;
    modsecurity_rules_file /etc/nginx/modsec/modsecurity.conf;

    location / {
        proxy_pass http://site1_backend;
    }
}

Why this is recommended:

  • one policy map in nginx.conf,
  • one enforcement snippet,
  • easy rollout across many domains,
  • lower risk of config drift.

4. Alternative Per-Site Enforcement

If you only run one or two sites, you can enforce directly in each server block:

server {
    server_name Site2.mouradcloud.com;

    if ($allowed_country = no) {
        return 444;
    }

    location / {
        proxy_pass http://site2_backend;
    }
}

This works, but becomes harder to maintain as site count grows.

5. Keep ModSecurity Enabled

GeoIP and ModSecurity are different controls and should run together:

  • GeoIP blocks by country code.
  • ModSecurity blocks malicious request content.

Typical directives:

modsecurity on;
modsecurity_rules_file /etc/nginx/modsec/modsecurity.conf;

6. Validate and Reload Safely

sudo nginx -t
sudo systemctl reload nginx

7. Command to List IPs That Tried to Connect or Hit Errors

Use this one-liner to aggregate IPs from both access and error logs:

sudo awk 'FILENAME ~ /access/ {ips[$1]++} FILENAME ~ /error/ {while (match($0,/([0-9]{1,3}\.){3}[0-9]{1,3}/)) {ip=substr($0,RSTART,RLENGTH); ips[ip]++; $0=substr($0,RSTART+RLENGTH)}} END {for (ip in ips) print ips[ip], ip}' /var/log/nginx/*access*.log /var/log/nginx/*error*.log | sort -nr

What it does:

  • Counts source IPs from access logs (attempted requests).
  • Extracts IPs found in error logs.
  • Merges and sorts by highest activity first.

8. Optional: Focus on 4xx and 5xx Only

sudo awk '$9 ~ /^[45][0-9][0-9]$/ {ips[$1]++} END {for (ip in ips) print ips[ip], ip}' /var/log/nginx/*access*.log | sort -nr

This helps quickly identify suspicious clients that generated failures.

Final Takeaway

For long-term operations, use global policy plus centralized snippet-based enforcement:

  • global country map in nginx.conf,
  • shared GeoIP block snippet for all vhosts,
  • ModSecurity for payload-level protection,
  • and log analysis commands for visibility.

This gives cleaner operations than repeating custom logic in each individual site file.