Integrations
Traefik plugin
A middleware from the Traefik Plugin Catalog that checks each visitor before the router passes the request on.
Install
- 1Declare the plugin in Traefik's static configuration and restart Traefik.
- 2Add a Traefik site in the dashboard and copy its Site ID.
- 3Set
IPSCANNER_API_KEYon the Traefik container. - 4Add an
ipscannermiddleware with the Site ID and put it on a router.
A new site starts in monitor mode: it adds headers and blocks nothing until you switch it to enforce.
Overview on the Traefik plugin page, source and full examples on GitHub.
experimental:
plugins:
ipscanner:
moduleName: github.com/ipscanner/ipscanner-traefik
version: v0.1.0Coolify, Compose, Kubernetes
- Coolify
Plugin flags in the proxy's command list and the key in its environment. Then the middleware in your app's container labels, appended to each router Coolify generated. Use one middleware name per app.
- Docker Compose
Flags on the Traefik service, the middleware in your app's labels. Other routers reuse it as
ipscanner@docker. - Kubernetes
A Middleware resource with the key from a Secret. Reference it from an IngressRoute, or from an Ingress with the
router.middlewaresannotation. - File provider
The middleware in a dynamic configuration file, with the key from a mounted secret.
Without a key every request passes with X-IPScanner-Status: skipped.
- '--experimental.plugins.ipscanner.modulename=github.com/ipscanner/ipscanner-traefik'
- '--experimental.plugins.ipscanner.version=v0.1.0'
environment:
- IPSCANNER_API_KEY=pk_live_...Configuration
apiKeyAPI key. Plugin options show in the Traefik dashboard, so prefer
IPSCANNER_API_KEY,apiKeyFileor aurn:k8s:secretreference.apiKeyFileFile holding the API key, such as a mounted Docker or Kubernetes secret.
siteIdSite ID from the dashboard. Mode and policy then come from the dashboard.
modedefault monitormonitor or enforce. With a site, only monitor has an effect: it keeps the site in monitor mode.
blockClassesdefault malicious_automationClasses blocked in enforce mode without a site: malicious_automation, ai_agent, tor, vpn, proxy, hosting, relay or none.
trustedProxiesAddresses and CIDR ranges whose IP headers are trusted. private adds the private ranges, cloudflare the Cloudflare ranges.
ipHeadersdefault X-Forwarded-ForHeaders read from a trusted proxy, in order. Behind Cloudflare:
CF-Connecting-IP.timeoutdefault 1500msBudget for one check. On a timeout the request passes.
cacheTTLdefault 10mHow long a verdict is reused per site, IP and user agent.
cacheSizedefault 10000Verdicts kept in memory.
policyTTLdefault 30sHow long the site policy is reused.
skipPathsdefault static assetsCase-insensitive regex on the path. Matching requests are not checked; none checks every path.
blockMessagedefault Blocked by IPScanner edge guard.Text of the 403 page.
debugdefault falseAdds
X-IPScanner-Status,-Classand-Actionto responses and logs one line per decision.apiUrldefault https://ipscanner.ioAPI base URL.
Lists take YAML lists or comma-separated values (blockClasses=tor,vpn in labels). Durations use Go syntax: 1500ms, 30s, 10m. An invalid option disables the router with an error in the Traefik log.
Client IP
The visitor is the address Traefik sees, unless that address is a trusted proxy.
trustedProxiesX-Forwarded-For, or theipHeaderslist, is read only from these addresses.forwardedHeaders.trustedIPsTraefik drops
X-Forwarded-Forfrom peers outside the entry point's trusted IPs, so set both, or readCF-Connecting-IP.trustedProxies=cloudflare,ipHeaders=CF-Connecting-IPBehind Cloudflare with the orange cloud on, add both to the middleware.
Headers it adds
Added to the request your app receives. Incoming X-IPScanner-* headers are removed first, so a client cannot send its own verdict.
X-IPScanner-Statusok, error, timeout, backoff, skippedWhether the check ran, and if not, why.
X-IPScanner-Classhuman, verified_bot, ai_agent, …Traffic class of the visitor.
X-IPScanner-Actionallow, flag, block, would_flag, would_blockWhat happened to the request. Monitor mode gives
would_flagandwould_block.X-IPScanner-Network-Classresidential_clean, hosting, vpn, tor, …Network class of the address.
X-IPScanner-Anonymizedtrue, falseThe network hides where the visitor is.
X-IPScanner-Risk0-100Risk score of the address.
X-IPScanner-CountryDECountry of the address.
X-IPScanner-Sitesite_...The Site ID, when one is set.
When a check fails or is skipped, only X-IPScanner-Status is set.
X-IPScanner-Status: ok
X-IPScanner-Class: ai_agent
X-IPScanner-Action: would_block
X-IPScanner-Network-Class: hosting
X-IPScanner-Anonymized: true
X-IPScanner-Risk: 60
X-IPScanner-Country: US
X-IPScanner-Site: site_4fQ8nZ2kLm7xR1vT9cBwCaching and fail open
- Site policyGET /v1/sites/{id}/policy
Mode and per-class policy, cached 30 s. If a refresh fails, the last good policy stays for 24 hours. Changes apply without a reload.
- Verdict cache
One check per visitor (site, IP, user agent) per 10 minutes, in memory, up to 10,000 visitors. Kept across configuration reloads.
- Fail open
1.5 s for verdict and policy together. On a timeout or API error the request passes and checks pause for 30 s, or 5 minutes after a 401, 402, 403 or 429.
- Always pass
Verified crawlers, OPTIONS requests, private and loopback addresses, and paths on the skip list.
- Blocked
A 403 page with the request ID. Monitor mode never blocks.
- Free plan
Sites on the Free plan stay in monitor mode.
- Cost
2 request units per uncached visitor. Repeat requests inside the cache window are free.
What it sends
- POST /v1/edge/checkCounts as 2 requests
One call per uncached visitor.
- GET /v1/sites/{id}/policyNot metered
The site policy, when a Site ID is set.
X-IPScanner-Source: traefikMarks the calls as this integration's in your traffic and usage.
User-Agent: ipscanner-traefik/<version>The version, shown on the site's page in the dashboard.
Request body
sitestringThe Site ID, when one is set.
ipstringThe visitor's address.
user_agentstringThe visitor's User-Agent.
headersobjectAccept, Accept-Language, Accept-Encoding, Sec-CH-UA* and Sec-Fetch-*, each cut to 512 bytes.
request_idstringThe request's
X-Request-Idor a new ID, also printed on the block page.
curl https://ipscanner.io/v1/edge/check \
-H "Authorization: Bearer $IPSCANNER_API_KEY" \
-H "X-IPScanner-Source: traefik" \
-H "User-Agent: ipscanner-traefik/0.1.0" \
-H "Content-Type: application/json" \
-d '{
"site": "site_4fQ8nZ2kLm7xR1vT9cBw",
"ip": "203.0.113.7",
"user_agent": "Mozilla/5.0",
"headers": {
"accept-language": "en-US"
},
"request_id": "9f2c41b7d03e77f2"
}'