# Nginx GeoIP Not Working

GeoIP should block countries, route based on location, or show region-specific content. Instead, $geoip_country_code is empty, country blocks don't work, or all requests come from the same detected country. GeoIP functionality depends on module availability, database configuration, and correct usage.

Understanding Nginx GeoIP

  1. 1.Nginx GeoIP requires:
  2. 2.GeoIP module compiled into Nginx (ngx_http_geoip_module)
  3. 3.MaxMind GeoIP database files
  4. 4.geoip_country or geoip_city directive
  5. 5.Variables like $geoip_country_code

Check module availability: ``bash nginx -V 2>&1 | grep -o geoip # Should show: --with-http_geoip_module

If missing, you need to recompile Nginx with the module or install the package version that includes it.

Common Cause 1: Module Not Compiled/Installed

The GeoIP module isn't available in your Nginx build.

Diagnosis: ```bash # Check if module is compiled nginx -V 2>&1 | grep geoip

# Check installed modules (some distributions use dynamic modules) ls /etc/nginx/modules/ ls /usr/lib/nginx/modules/

# Check if module is loaded grep -r "load_module" /etc/nginx/nginx.conf ```

Solution:

For dynamic modules: ``nginx # In nginx.conf main context load_module modules/ngx_http_geoip_module.so;

For missing module: ```bash # Ubuntu/Debian - install extra modules sudo apt install libnginx-mod-http-geoip

# Or install full nginx sudo apt install nginx-full

# CentOS/RHEL sudo yum install nginx-module-geoip ```

For source compilation: ``bash ./configure --with-http_geoip_module make sudo make install

Common Cause 2: GeoIP Database Not Configured

The database path isn't set or the database file doesn't exist.

Problematic config: ``nginx http { geoip_country GeoIP.dat; # Where is this file? }

Diagnosis: ```bash # Check if database exists ls -la /usr/share/GeoIP/

# Common locations ls -la /var/lib/GeoIP/ ls -la /usr/local/share/GeoIP/

# Check Nginx config for database path grep -r "geoip" /etc/nginx/ ```

Solution: Specify full path: ```nginx http { geoip_country /usr/share/GeoIP/GeoIP.dat;

# For city data geoip_city /usr/share/GeoIP/GeoLiteCity.dat; } ```

Common Cause 3: GeoIP Database Missing or Outdated

The database files don't exist or are too old.

Diagnosis: ```bash # Check database files ls -la /usr/share/GeoIP/

# Check database age stat /usr/share/GeoIP/GeoIP.dat

# Test database with geoiplookup geoiplookup 8.8.8.8 ```

Solution: Install GeoIP database: ```bash # Ubuntu/Debian sudo apt install geoip-bin geoip-database

# CentOS/RHEL sudo yum install geoip geoip-data

# Download from MaxMind (GeoLite2 requires free account) wget https://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.tar.gz tar xzf GeoLite2-Country.tar.gz sudo mv GeoLite2-Country.mmdb /usr/share/GeoIP/ ```

Note: GeoIP Legacy is deprecated. Use GeoIP2 for current data.

Common Cause 4: Using GeoIP2 with Legacy Module

The GeoIP module only supports legacy database format, not GeoIP2/MMDB.

Problem: ``nginx # Legacy module geoip_country /usr/share/GeoIP/GeoLite2-Country.mmdb; # This won't work - .mmdb format requires different module

Solution: Use GeoIP2 module: ```bash # Install geoip2 module sudo apt install libnginx-mod-http-geoip2

# Or compile with geoip2 ./configure --add-dynamic-module=/path/to/ngx_http_geoip2_module ```

```nginx # Load module load_module modules/ngx_http_geoip2_module.so;

http { geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb { $geoip2_country_code country iso_code; $geoip2_country_name country names en; } } ```

Common Cause 5: GeoIP Variables Empty

Variables like $geoip_country_code return empty values.

Diagnosis: ```bash # Add header to see value curl -H "X-Forwarded-For: 8.8.8.8" http://localhost/test | grep Country

# Or in Nginx config add_header X-Country $geoip_country_code; ```

Common causes:

  1. 1.Database not loaded:
  2. 2.```bash
  3. 3.sudo nginx -t
  4. 4.# Should not show "geoip_country" errors
  5. 5.`
  6. 6.IP is private (not in database):
  7. 7.Private IPs (192.168.x.x, 10.x.x.x) have no country.
nginx
# Log to see detected IP
add_header X-Detected-IP $remote_addr;
  1. 1.Behind proxy, real IP not detected:
  2. 2.```nginx
  3. 3.# Use real IP from header
  4. 4.set_real_ip_from 10.0.0.0/8;
  5. 5.real_ip_header X-Forwarded-For;

geoip_country /usr/share/GeoIP/GeoIP.dat; ```

  1. 1.Module variables changed:
  2. 2.GeoIP2 uses different variable names:
  3. 3.```nginx
  4. 4.geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb {
  5. 5.$geoip_country_code country iso_code; # Not $geoip_country_code
  6. 6.}
  7. 7.`

Common Cause 6: Country Blocking Not Working

Country-based blocks don't apply.

Problematic config: ```nginx http { geoip_country GeoIP.dat;

# Block specific countries if ($geoip_country_code = CN) { return 403; } } ```

Problems: - if directive issues in Nginx - Variable might be empty - Country code format (CN vs cn)

Solution: Use geo/map for blocking: ```nginx http { geoip_country /usr/share/GeoIP/GeoIP.dat;

# Define blocked countries geo $blocked_country { default 0; CN 1; RU 1; KP 1; }

map $geoip_country_code $block_request { default 0; CN 1; RU 1; KP 1; }

server { location / { if ($block_request) { return 403; }

add_header X-Country $geoip_country_code always; } } } ```

Better approach using map: ```nginx http { geoip_country /usr/share/GeoIP/GeoIP.dat;

map $geoip_country_code $allowed { default yes; CN no; RU no; }

server { location / { if ($allowed = no) { return 403 "Country blocked"; } } } } ```

Common Cause 7: GeoIP City Not Working

City-level detection returns empty values.

Diagnosis: ```nginx http { geoip_city /usr/share/GeoIP/GeoLiteCity.dat;

server { location /test { add_header X-City $geoip_city; add_header X-Region $geoip_region; add_header X-Country $geoip_city_country_code; return 200; } } } ```

bash
curl -H "X-Forwarded-For: 8.8.8.8" -I http://localhost/test
  1. 1.Causes:
  2. 2.Database file doesn't exist
  3. 3.Using country database instead of city database
  4. 4.IP doesn't have city-level resolution
  5. 5.GeoIP2 uses different variable mapping

GeoIP2 city configuration: ``nginx geoip2 /usr/share/GeoIP/GeoLite2-City.mmdb { $geoip2_city city names en; $geoip2_region subdivisions 0 iso_code; $geoip2_country country iso_code; $geoip2_latitude location latitude; $geoip2_longitude location longitude; }

Common Cause 8: GeoIP Behind CDN/Proxy

All requests appear from same CDN IP.

Problem: ``nginx geoip_country GeoIP.dat; # $remote_addr is CDN IP, not user IP

Solution: Use real IP module: ```nginx # Set trusted proxies set_real_ip_from 103.21.244.0/22; # Cloudflare set_real_ip_from 103.22.200.0/22; set_real_ip_from 173.245.48.0/20; # Add all CDN/proxy IPs

real_ip_header CF-Connecting-IP; # Cloudflare # or real_ip_header X-Forwarded-For;

real_ip_recursive on;

geoip_country /usr/share/GeoIP/GeoIP.dat; ```

Now $remote_addr contains the user's real IP.

Verification Steps

  1. 1.Check module:
  2. 2.```bash
  3. 3.nginx -V 2>&1 | grep geoip
  4. 4.`
  5. 5.Check database:
  6. 6.```bash
  7. 7.ls -la /usr/share/GeoIP/
  8. 8.`
  9. 9.Test GeoIP lookup:
  10. 10.```nginx
  11. 11.location /geoip-test {
  12. 12.add_header X-Country $geoip_country_code;
  13. 13.return 200;
  14. 14.}
  15. 15.`

```bash curl -H "X-Forwarded-For: 8.8.8.8" -I http://localhost/geoip-test # Should show X-Country: US

curl -H "X-Forwarded-For: 1.1.1.1" -I http://localhost/geoip-test # Should show X-Country: AU ```

  1. 1.Check Nginx configuration:
  2. 2.```bash
  3. 3.sudo nginx -t
  4. 4.`
  5. 5.Monitor logs:
  6. 6.```nginx
  7. 7.log_format geo '$remote_addr - $geoip_country_code [$time_local]';
  8. 8.access_log /var/log/nginx/geo.log geo;
  9. 9.`

Complete Working Configuration

```nginx load_module modules/ngx_http_geoip2_module.so;

http { geoip2 /usr/share/GeoIP/GeoLite2-Country.mmdb { $geoip_country_code country iso_code; $geoip_country_name country names en; }

geoip2 /usr/share/GeoIP/GeoLite2-City.mmdb { $geoip_city city names en; $geoip_region subdivisions 0 names en; $geoip_latitude location latitude; $geoip_longitude location longitude; }

# Trust CDN/proxy IPs set_real_ip_from 10.0.0.0/8; set_real_ip_from 172.16.0.0/12; set_real_ip_from 192.168.0.0/16; real_ip_header X-Forwarded-For; real_ip_recursive on;

# Country blocking map $geoip_country_code $blocked { default 0; CN 1; RU 1; }

# Geographic routing map $geoip_country_code $backend { default backend-eu; US backend-us; CA backend-us; GB backend-eu; DE backend-eu; }

upstream backend-eu { server eu.example.com; }

upstream backend-us { server us.example.com; }

server { location / { if ($blocked) { return 403 "Country blocked"; }

add_header X-Country $geoip_country_code always; proxy_pass http://$backend; } } } ```

Quick Reference

IssueCauseFix
Variables emptyModule not loadedInstall/load module
Variables emptyDatabase missingInstall GeoIP database
Wrong countryBehind proxyConfigure real_ip_header
City not detectedCity database missingInstall city database
mmdb not workingLegacy moduleUse geoip2 module
Block not workingEmpty variableUse map for blocking
All same countryPrivate IPs onlyTest with public IP

GeoIP requires module, database, and correct configuration chain. Start by verifying the module is loaded and database exists, then test with known public IPs.