Traefik Classifier
Traefik plugin
that registers a custom middleware
for getting data from
MaxMind GeoIP databases
and classifying traffic by type (datacenter, VPN, Tor, AI crawler).
All results are passed downstream via HTTP request headers.
Features
- GeoIP lookups — City, Country, ASN data from MaxMind GeoLite2/GeoIP2 databases
- Datacenter detection — ASN-based, using bad-asn-list
- VPN detection — CIDR-based, using X4BNet/lists_vpn
- Tor exit node detection — IP-based, using torproject.org bulk exit list
- AI crawler detection — User-Agent based (GPTBot, ClaudeBot, Google-Extended, Bytespider, etc.)
- Fail-safe — Classification errors are caught with
recover(); traffic always flows
- Auto-refresh — Data files are reloaded periodically (default: every 6 hours)
Supports both
GeoIP2
and
GeoLite2 databases.
Breaking Changes
[!IMPORTANT]
Header rename: All GeoIP headers changed from GeoIP-* to X-GeoIP-* (e.g. GeoIP-Country → X-GeoIP-Country).
Update any downstream consumers that read these headers.
| Header |
Description |
X-GeoIP-Country |
Country name (e.g. "Germany") |
X-GeoIP-Country-Code |
ISO country code (e.g. "DE") |
X-GeoIP-Region |
Region name (e.g. "Bavaria") |
X-GeoIP-Region-Code |
Region code (e.g. "BY") |
X-GeoIP-City |
City name (e.g. "Munich") |
X-GeoIP-Postal-Code |
Postal code |
X-GeoIP-Latitude |
Latitude |
X-GeoIP-Longitude |
Longitude |
X-GeoIP-Accuracy-Radius |
Accuracy radius in meters |
X-GeoIP-Geohash |
Geohash |
X-GeoIP-Continent |
Continent name |
X-GeoIP-Continent-Code |
Continent code |
X-GeoIP-ASN-System-Number |
ASN number (e.g. "16509") |
X-GeoIP-ASN-Organization |
ASN organization (e.g. "AMAZON-02") |
X-GeoIP-IPAddress |
Resolved client IP address |
| Header |
Values |
Description |
X-Traffic-Type |
residential, datacenter, vpn, tor, ai-crawler |
Overall traffic classification |
X-Traffic-Datacenter |
true / false |
IP belongs to a known datacenter ASN |
X-Traffic-VPN |
true / false |
IP belongs to a known VPN range |
X-Traffic-Tor |
true / false |
IP is a known Tor exit node |
X-Traffic-AI-Bot |
true / false |
User-Agent matches a known AI crawler |
Priority order for X-Traffic-Type: tor > vpn > ai-crawler > datacenter > residential.
Individual flags are independent — a request can be both X-Traffic-Datacenter: true and X-Traffic-Tor: true.
Installation
Docker Compose
Simple example to run in docker compose with auto download maxmind database
docker-compose.yaml
services:
traefik:
image: "traefik:v3.2"
container_name: "traefik"
restart: unless-stopped
command:
#- "--log.level=DEBUG"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entryPoints.web.address=:80"
- "--experimental.localPlugins.traefikgeoip.moduleName=github.com/WebConcern/traefik-classifier"
ports:
- "80:80"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "../.:/plugins-local/src/github.com/WebConcern/traefik-classifier"
- geoipupdate_data:/usr/share/GeoIP
networks:
- traefikgeoip_example
whoami:
image: "traefik/whoami"
container_name: "traefik-whoami"
restart: unless-stopped
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`localhost`)"
- "traefik.http.routers.whoami.entrypoints=web"
- "traefik.http.middlewares.traefikgeoip.plugin.traefikgeoip.cityDbPath=/usr/share/GeoIP/GeoLite2-City.mmdb"
- "traefik.http.middlewares.traefikgeoip.plugin.traefikgeoip.asnDbPath=/usr/share/GeoIP/GeoLite2-ASN.mmdb"
- "traefik.http.middlewares.traefikgeoip.plugin.traefikgeoip.lightMode=true"
- "traefik.http.middlewares.traefikgeoip.plugin.traefikgeoip.ipHeader=X-IP"
- "traefik.http.middlewares.traefikgeoip.plugin.traefikgeoip.iso88591=true"
# - "traefik.http.middlewares.traefikgeoip.plugin.traefikgeoip.preferXForwardedForHeader=true"
# - "traefik.http.middlewares.traefikgeoip.plugin.traefikgeoip.failInError=true"
- "traefik.http.routers.whoami.middlewares=traefikgeoip"
networks:
- traefikgeoip_example
geoipupdate:
container_name: cii_geoipupdate
image: ghcr.io/maxmind/geoipupdate
restart: unless-stopped
# restart: on-failure
env_file:
- ./maxmind.env
volumes:
- geoipupdate_data:/usr/share/GeoIP
networks:
- traefikgeoip_example
volumes:
geoipupdate_data:
networks:
traefikgeoip_example:
name: traefikgeoip_example
driver: bridge
Request your free maxmind credentials in site and up in
maxmind.env
GEOIPUPDATE_ACCOUNT_ID=6025842
GEOIPUPDATE_LICENSE_KEY=abQ2rY_UMmru2Gd6LrbfGcrmKFwvR1duEDPZ_nmw
GEOIPUPDATE_EDITION_IDS=GeoLite2-ASN GeoLite2-City GeoLite2-Country
GEOIPUPDATE_FREQUENCY=72
Kubernetes
The tricky part of installing this plugin into containerized environments, like Kubernetes,
is that a container should contain a database within it.
[!WARNING]
Setup below is provided for demonstration purpose and should not be used on production.
Traefik's plugin site is observed to be frequently unavailable,
so plugin download may fail on pod restart.
Tested with official Traefik chart version 26.0.0.
The following snippet should be added to values.yaml:
experimental:
plugins:
traefik-classifier:
moduleName: github.com/WebConcern/traefik-classifier
version: v0.1.0
deployment:
additionalVolumes:
- name: geoip2
emptyDir: {}
initContainers:
- name: download
image: alpine
volumeMounts:
- name: geoip2
mountPath: /tmp/geoip2
command:
- "/bin/sh"
- "-ce"
- |
wget -P /tmp https://raw.githubusercontent.com/thiagotognoli/traefikgeoip/main/geolite2.tgz
tar --directory /tmp/geoip2 -xvzf /tmp/geolite2.tgz
additionalVolumeMounts:
- name: geoip2
mountPath: /geoip2
Create Traefik Middleware
GeoIP only:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: traefik-classifier
namespace: traefik
spec:
plugin:
traefik-classifier:
cityDbPath: "/geoip2/GeoLite2-City.mmdb"
asnDbPath: "/geoip2/GeoLite2-ASN.mmdb"
GeoIP + traffic classification:
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
name: traefik-classifier
namespace: traefik
spec:
plugin:
traefik-classifier:
cityDbPath: "/geoip2/GeoLite2-City.mmdb"
asnDbPath: "/geoip2/GeoLite2-ASN.mmdb"
datacenterFile: "/data/traffic-classifier/bad-asn-list.csv"
vpnFile: "/data/traffic-classifier/vpn-ipv4.txt"
torFile: "/data/traffic-classifier/tor-exits.txt"
refreshSeconds: 21600
Configuration
| Name |
Description |
Default |
| cityDbPath |
Container path to City GeoIP database. |
"" |
| countryDbPath |
Container path to Country GeoIP database. |
"" |
| asnDbPath |
Container path to ASN GeoIP database. |
"" |
| preferXForwardedForHeader |
Use X-Forwarded-For header to extract IP address. |
false |
| ipHeader |
Alternate header for IP. |
"" |
| failInError |
Fail to start plugin on error. |
false |
| debug |
Enable debug messages. |
false |
| iso88591 |
Encode in ISO-8859-1. |
false |
| lightMode |
Reduce headers to essential fields only. |
false |
| datacenterFile |
Path to CSV file with datacenter ASNs (e.g. bad-asn-list.csv). |
"" |
| vpnFile |
Path to text file with VPN CIDR ranges. |
"" |
| torFile |
Path to text file with Tor exit node IPs. |
"" |
| aiBotFile |
Path to text file with AI bot UA substrings (one per line). Overrides built-in list. |
"" |
| refreshSeconds |
Seconds between data file reloads. |
21600 |
Data Sources
The traffic classifier uses externally maintained lists:
| Detection |
Source |
Format |
| Datacenter ASNs |
bad-asn-list |
CSV (first column = ASN number) |
| VPN ranges |
X4BNet/lists_vpn |
One CIDR per line |
| Tor exit nodes |
torproject.org |
One IP per line |
| AI crawlers |
Built-in (or file via aiBotFile) |
User-Agent substring match |
AI Crawlers Detected
GPTBot, ChatGPT-User, OAI-SearchBot, ClaudeBot, Claude-Web, Google-Extended, Bytespider, CCBot, FacebookBot, anthropic-ai, PerplexityBot, Cohere-ai, meta-externalagent
Development
Install Go, golangci-lint, yaegi and just
brew install go golangci-lint just
go install github.com/traefik/yaegi/cmd/yaegi@latest
To run linter and tests execute this command
just test
Original Plugin
This project is a fork of https://github.com/thiagotognoli/traefikgeoip
which is itself a fork of https://github.com/traefik-plugins/traefikgeoip2
and some parts of https://github.com/Maronato/traefik_geoip