Setting Up the AI Bot Blocker
Configuration
Enable Bot Blocker
- Open the route (Edit modal → Security tab)
- Enable the AI Bot Blocker toggle
- Choose a mode (see below)
- Save the route
The Bot Blocker can also be enabled when creating a new route (in the "Create new route" card).
Available modes
| Mode | Behavior | Use case |
|---|---|---|
| Block (403) | Returns HTTP 403 Forbidden | Default — clear and unambiguous |
| Tarpit | Responds extremely slowly (drip-feed) | Wastes crawler resources, ties up their connections |
| Drop | Immediately terminates the TCP connection | Most aggressive option, no response |
| Garbage | Sends random data as the response | Poisons the crawler's training data |
| Redirect (308) | Redirects to a different URL | E.g. to an "access denied" page |
| Custom | Custom message with a configurable status code | Flexible — e.g. 451 "Unavailable For Legal Reasons" |
Mode-specific settings
Redirect mode
- Redirect URL (required): The target URL for the redirect (must start with
http://orhttps://)
Custom mode
- Response message: The text sent to the bot (max. 500 characters)
- Status code: HTTP status code of the response (100-599, default: 403)
Bot counter
How it works
A background task counts the blocked requests per route every 60 seconds. The counter is based on HTTP 403 responses in the Caddy access log, filtered by the route's domain.
Display
An orange badge is shown in the route list:
- Bot icon + number (e.g.
🤖 42): Number of requests blocked so far - Bot icon only (no number): Bot Blocker is active, but no bots have been blocked yet
The badge is only shown for HTTP routes (not for L4/TCP routes).
Known limitation
The counter counts all HTTP 403 responses on the route, not only those from the Bot Blocker. If IP access control or ACL is also active on the same route, these can produce 403 responses that are counted as well. The accuracy is sufficient for most use cases.
API
Create/edit route
The Bot Blocker settings are controlled via the existing route API:
# Enable Bot Blocker (block mode)
curl -X PUT /api/v1/routes/:id \
-H "Content-Type: application/json" \
-d '{
"bot_blocker_enabled": true,
"bot_blocker_mode": "block"
}'
# Redirect mode
curl -X PUT /api/v1/routes/:id \
-H "Content-Type: application/json" \
-d '{
"bot_blocker_enabled": true,
"bot_blocker_mode": "redirect",
"bot_blocker_config": "{\"url\": \"https://example.com/blocked\"}"
}'
# Custom mode
curl -X PUT /api/v1/routes/:id \
-H "Content-Type: application/json" \
-d '{
"bot_blocker_enabled": true,
"bot_blocker_mode": "custom",
"bot_blocker_config": "{\"message\": \"AI crawlers are not welcome\", \"status_code\": 451}"
}'
API fields
| Field | Type | Description |
|---|---|---|
bot_blocker_enabled |
Boolean | Bot Blocker enabled/disabled |
bot_blocker_mode |
String | Mode: block, tarpit, drop, garbage, redirect, custom |
bot_blocker_config |
String (JSON) | Mode-specific configuration |
bot_blocker_count |
Integer (read-only) | Number of blocked requests (only in GET response) |
Testing
Verify bot blocking
# Normal request — should go through
curl -s -o /dev/null -w "%{http_code}" https://your-route.com/
# Expected result: 200 (or 302 on auth)
# Simulate a request from an OpenAI IP (only possible on the local network)
# Instead: look for "defender" entries in the GateControl log
docker logs gatecontrol 2>&1 | grep "defender"
Check the counter
# Fetch route data — bot_blocker_count contains the current counter
curl -s /api/v1/routes/:id | jq '.route.bot_blocker_count'
Limitations
- HTTP routes only: L4/TCP routes do not support bot blocking (caddy-defender is an HTTP handler)
- IP-based: Blocking is based on IP addresses, not User-Agent strings. Bots coming from non-listed IP ranges are not detected.
- No custom IP ranges: The default ranges maintained by the plugin are used
- No whitelist: Individual IPs cannot be exempted from blocking
- Counter accuracy: Counts all 403s, not just bot blocks (see above)
Database
Fields in the routes table
| Column | Type | Default | Description |
|---|---|---|---|
bot_blocker_enabled |
INTEGER | 0 | Feature enabled (0/1) |
bot_blocker_mode |
TEXT | 'block' | Active mode |
bot_blocker_count |
INTEGER | 0 | Cumulative block counter |
bot_blocker_config |
TEXT | null | JSON with mode-specific options |
Migration
Version 28 (add_bot_blocker) — created on 2026-03-28.
Backup/Restore
The Bot Blocker configuration (bot_blocker_enabled, bot_blocker_mode, bot_blocker_config) is included in backup/restore. The bot_blocker_count is not exported — the counter starts at 0 after a restore.
Technical details
Caddy handler config
{
"handler": "defender",
"raw_responder": "block",
"ranges": ["openai", "aws", "gcloud", "githubcopilot", "deepseek", "azurepubliccloud"]
}
Handler position in the route chain
1. defender (Bot Blocker) ← blocks bots immediately
2. trace (Request Tracing)
3. headers (Custom Headers)
4. rate_limit
5. mirror (Request Mirroring)
6. encode (compression)
7. reverse_proxy (backend)
Go module
pkg.jsn.cam/caddy-defender (originally github.com/JasonLovesDoggo/caddy-defender)
Background task
- Interval: 60 seconds
- Source:
/data/caddy/access.log - Logic: Parses JSON lines, filters by
status === 403, matchesrequest.hostagainst routes withbot_blocker_enabled, incrementsbot_blocker_count - Log rotation: Timestamp-based tracking (no offset), compatible with Caddy's log rotation (10 MB, 3 files)