A Traefik middleware plugin that blocks or allows traffic based on the geographic location (country) of the client's IP address.
- 🌍 Country-based filtering: Allow or block traffic from specific countries
- 🚀 High performance: Built-in caching to minimize API calls
- 🔒 Flexible configuration: Support for both allowlists and blocklists
- 📊 Grafana metrics: Privacy-respecting structured logging for traffic analytics
- 🏢 Organization tracking: Log ISP/company information without exposing IPs
- 🔄 Proxy support: Handles X-Forwarded-For and X-Real-IP headers
- 🎯 Default actions: Configure default behavior for unknown countries
- 💾 Smart caching: Configurable cache duration to optimize performance
- 🗑️ Automatic cleanup: Built-in log retention and rotation
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
allowedCountries |
[]string | No | [] | List of ISO 3166-1 alpha-2 country codes to allow (e.g., US, GB, DE). Supports comma-separated values in a single string (e.g., "IT,US,DE") |
blockedCountries |
[]string | No | [] | List of ISO 3166-1 alpha-2 country codes to block. Supports comma-separated values in a single string (e.g., "CN,RU,KP") |
queryURL |
string | No | https://ipapi.co/{ip}/json/ |
GeoIP lookup API URL (use {ip} placeholder) |
cacheDuration |
int | No | 60 | Cache duration in minutes |
defaultAction |
string | No | allow | Default action for unknown countries: allow or block |
blockMessage |
string | No | Access denied from your country | Message shown to blocked users |
logBlocked |
bool | No | true | Legacy stdout logging (includes IPs) |
trustedProxies |
[]string | No | [] | List of trusted proxy IP addresses/CIDR ranges. Use "cloudflare" to automatically fetch and trust Cloudflare IP ranges |
| Option | Type | Required | Default | Description |
|---|---|---|---|---|
enableMetricsLog |
bool | No | false | Enable Grafana-compatible metrics logging |
metricsLogPath |
string | No | /var/log/traefik-geoblock/metrics.log |
Path for metrics log file |
metricsFlushSeconds |
int | No | 60 | How often to flush aggregated metrics to disk |
logRetentionDays |
int | No | 14 | Days to retain log files before automatic cleanup |
Note: For detailed integration instructions, see:
- GRAFANA-METRICS.md - Grafana Loki integration and dashboards
- PROMETHEUS-INTEGRATION.md - Prometheus metrics with secure remote scraping
cd traefik
mkdir plugins
cd plugins
git clone https://github.com/CangioUni/traefik-geoblock-plugin.gitAdd the plugin to your Traefik static configuration (traefik.yml):
experimental:
plugins:
geoblock:
moduleName: github.com/CangioUni/traefik-geoblock-plugin
version: v0.1.0Add folder to compose configuration (docker-compose.yml):
services:
traefik:
volumes:
- /home/user/dockers/traefik/plugins/traefik-geoblock-plugin:/plugins-local/src/github.com/CangioUni/traefik-geoblock-plugin/Only allow traffic from IT and DE:
http:
middlewares:
geoblock-allowlist:
plugin:
geoblock:
allowedCountries:
- IT
- DE
blockMessage: "Access is only available from US, CA, and GB"
logBlocked: true
routers:
my-router:
rule: "Host(`example.com`)"
service: my-service
middlewares:
- geoblock-allowlistBlock traffic from specific countries:
http:
middlewares:
geoblock-blocklist:
plugin:
geoblock:
blockedCountries:
- CN
- RU
- KP
defaultAction: allow
blockMessage: "Access from your region is restricted"
logBlocked: trueBlock all countries by default, only allow specific ones:
http:
middlewares:
geoblock-strict:
plugin:
geoblock:
allowedCountries:
- US
defaultAction: block
blockMessage: "Service not available in your country"
cacheDuration: 120Configure trusted proxies when behind load balancers:
http:
middlewares:
geoblock-with-proxy:
plugin:
geoblock:
allowedCountries:
- US
- CA
trustedProxies:
- "10.0.0.0/8"
- "172.16.0.0/12"
- "192.168.0.0/16"
logBlocked: trueAutomatically trust Cloudflare IP ranges when using Cloudflare as a reverse proxy:
http:
middlewares:
geoblock-cloudflare:
plugin:
geoblock:
allowedCountries:
- "IT,US,DE" # Comma-separated format
trustedProxies:
- "cloudflare" # Automatically fetches Cloudflare IP ranges
logBlocked: trueEnable privacy-respecting metrics for Grafana dashboards:
http:
middlewares:
geoblock-with-metrics:
plugin:
geoblock:
blockedCountries:
- CN
- RU
# Enable Grafana-compatible logging
enableMetricsLog: true
metricsLogPath: "/var/log/traefik-geoblock/metrics.log"
metricsFlushSeconds: 60
logRetentionDays: 14
# Disable IP logging for privacy
logBlocked: falseSee GRAFANA-METRICS.md for complete setup instructions and dashboard examples.
-
IP Extraction: The plugin extracts the client's IP address from the request, checking:
X-Forwarded-Forheader (skipping trusted proxies)X-Real-IPheader- Direct connection IP (
RemoteAddr)
-
Cache Check: Checks if the IP's country is already cached
-
GeoIP Lookup: If not cached, queries the configured GeoIP API
-
Decision: Determines if the request should be blocked based on:
- Allowlist (if configured, only these countries are allowed)
- Blocklist (if configured, these countries are blocked)
- Default action (for unknown countries)
-
Response: Either blocks the request (403 Forbidden) or passes it through
The plugin supports any GeoIP service that returns JSON with a country_code field. The default service is ipapi.co, which offers:
- Free tier: 30,000 requests per month
- No API key required
- Response format: JSON with country_code field
You can use other services by changing the databaseURL:
# ip-api.com (100 requests/minute free)
databaseURL: "http://ip-api.com/json/{ip}"
# ipinfo.io (requires API token)
databaseURL: "https://ipinfo.io/{ip}/json/?token=YOUR_TOKEN"
# ipgeolocation.io (requires API key)
databaseURL: "https://api.ipgeolocation.io/ipgeo?apiKey=YOUR_KEY&ip={ip}"- Caching: Set an appropriate
cacheDurationbased on your traffic patterns - API Rate Limits: Monitor your GeoIP service usage
- Private IPs: The plugin automatically allows private IP ranges (useful for development)
Use ISO 3166-1 alpha-2 country codes. Common examples:
| Country | Code | Country | Code |
|---|---|---|---|
| United States | US | Germany | DE |
| Canada | CA | France | FR |
| United Kingdom | GB | Spain | ES |
| Australia | AU | Italy | IT |
| Japan | JP | Brazil | BR |
| China | CN | India | IN |
| Russia | RU | Mexico | MX |
Full list: https://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
- Start the services:
docker-compose up -d- Test the service:
curl http://whoami.localhostYou can test with specific IPs by modifying headers:
# Test with a US IP
curl -H "X-Forwarded-For: 8.8.8.8" http://whoami.localhost
# Test with a CN IP
curl -H "X-Forwarded-For: 1.2.4.8" http://whoami.localhost- Ensure the plugin is properly tagged in GitHub
- Check Traefik logs for plugin loading errors
- Verify the
moduleNamematches your repository path
- Enable
logBlocked: trueto see what's happening - Check if IP is being correctly extracted (review logs)
- Verify country codes are uppercase (e.g.,
USnotus)
- Increase
cacheDurationto reduce API calls - Consider self-hosting a GeoIP database (like MaxMind GeoLite2)
- Check GeoIP service response times
- Spoofing: Configure
trustedProxiesproperly to prevent IP spoofing - Private IPs: The plugin automatically allows private IPs (development friendly)
- API Limits: Monitor your GeoIP service usage to avoid rate limiting
- Caching: Longer cache durations reduce API calls but may miss IP relocations
MIT License - feel free to use and modify as needed.
Contributions are welcome! Please feel free to submit a Pull Request.
For issues and questions, please use the GitHub issue tracker.