Skip to main content

JSON Schema

Caddy Defender can be configured using JSON in the Caddy JSON config file. The module ID is http.handlers.defender.
{
  "handler": "defender",
  "raw_responder": "block",
  "ranges": ["openai", "aws"],
  "whitelist": ["203.0.113.5"],
  "message": "Custom message",
  "status_code": 403,
  "url": "https://example.com/blocked",
  "serve_ignore": false,
  "tarpit_config": {
    "headers": {
      "Content-Type": "text/html"
    },
    "timeout": 30000000000,
    "bytes_per_second": 24,
    "code": 200
  }
}

Configuration Fields

raw_responder

raw_responder
string
required
Defines the response strategy for blocked requests.Valid values: "block", "custom", "drop", "garbage", "ratelimit", "redirect", "tarpit"
Returns a 403 Forbidden response with “Access denied” message.
{
  "handler": "defender",
  "raw_responder": "block",
  "ranges": ["openai", "aws"]
}

ranges

ranges
string[]
Specifies IP ranges to block using CIDR notation or predefined service keys.Default: ["aws", "gcloud", "azurepubliccloud", "openai", "deepseek", "githubcopilot"]
{
  "ranges": [
    "openai",
    "aws",
    "192.168.1.0/24",
    "10.0.0.0/8"
  ]
}
See IP Ranges documentation for all predefined service keys.

whitelist

whitelist
string[]
IP addresses to exclude from blocking. Only supports individual IPs, not ranges.Default: []
{
  "whitelist": [
    "203.0.113.5",
    "198.51.100.42"
  ]
}

message

message
string
Custom response message for the custom responder type.Required when: raw_responder is "custom"
{
  "raw_responder": "custom",
  "message": "You don't have permission to access this page"
}

status_code

status_code
integer
HTTP status code for the custom responder type.Default: 200Valid values: Any HTTP status code (200, 403, 404, 451, 503, etc.)
{
  "raw_responder": "custom",
  "message": "Page not found",
  "status_code": 404
}

url

url
string
Redirect URL for the redirect responder type.Required when: raw_responder is "redirect"
{
  "raw_responder": "redirect",
  "url": "https://example.com/blocked"
}

serve_ignore

serve_ignore
boolean
Serves a robots.txt file with Disallow: / directive to discourage crawlers.Default: false
{
  "serve_ignore": true
}

tarpit_config

tarpit_config
object
Configuration for the tarpit responder. Controls how data is streamed to slow down bots.Required when: raw_responder is "tarpit"

tarpit_config.headers

tarpit_config.headers
object
Custom HTTP headers to include in the tarpit response as key-value pairs.Default: {}
{
  "tarpit_config": {
    "headers": {
      "Content-Type": "text/html",
      "X-Custom-Header": "custom-value"
    }
  }
}

tarpit_config.timeout

tarpit_config.timeout
integer
Maximum duration (in nanoseconds) before forcefully closing the connection.Default: 30000000000 (30 seconds)Format: Duration in nanoseconds as int64Validation: Must be greater than 0
{
  "tarpit_config": {
    "timeout": 120000000000
  }
}
Common durations:
  • 30 seconds: 30000000000
  • 1 minute: 60000000000
  • 2 minutes: 120000000000
  • 5 minutes: 300000000000

tarpit_config.bytes_per_second

tarpit_config.bytes_per_second
integer
Number of bytes to stream per second. Lower values slow down bots more effectively.Default: 24Validation: Must be greater than 10
{
  "tarpit_config": {
    "bytes_per_second": 50
  }
}

tarpit_config.code

tarpit_config.code
integer
HTTP response code for the tarpit response.Default: 200
{
  "tarpit_config": {
    "code": 200
  }
}

Complete Examples

Block OpenAI and AWS

{
  "handler": "defender",
  "raw_responder": "block",
  "ranges": ["openai", "aws"]
}

Custom 403 Response

{
  "handler": "defender",
  "raw_responder": "custom",
  "message": "You don't have permission to access this page",
  "status_code": 403,
  "ranges": ["openai", "aws", "deepseek"]
}

Redirect to Blocked Page

{
  "handler": "defender",
  "raw_responder": "redirect",
  "url": "https://example.com/blocked",
  "ranges": ["vpn", "tor"]
}

Tarpit with Custom Configuration

{
  "handler": "defender",
  "raw_responder": "tarpit",
  "ranges": ["openai", "aws", "gcloud"],
  "tarpit_config": {
    "headers": {
      "Content-Type": "text/html",
      "X-Robots-Tag": "noindex, nofollow"
    },
    "timeout": 120000000000,
    "bytes_per_second": 30,
    "code": 200
  }
}

Whitelist Specific IPs

{
  "handler": "defender",
  "raw_responder": "block",
  "ranges": ["openai", "aws"],
  "whitelist": [
    "203.0.113.5",
    "198.51.100.42"
  ]
}

Serve robots.txt Disallow

{
  "handler": "defender",
  "raw_responder": "block",
  "ranges": ["openai", "aws", "gcloud"],
  "serve_ignore": true
}

Multiple Status Codes with Custom

{
  "handler": "defender",
  "raw_responder": "custom",
  "message": "Please contact support for API access",
  "status_code": 200,
  "ranges": ["openai"]
}

Full Caddy JSON Config Example

{
  "apps": {
    "http": {
      "servers": {
        "srv0": {
          "listen": [":443"],
          "routes": [
            {
              "match": [
                {
                  "host": ["example.com"]
                }
              ],
              "handle": [
                {
                  "handler": "defender",
                  "raw_responder": "tarpit",
                  "ranges": ["openai", "aws", "gcloud"],
                  "whitelist": ["203.0.113.5"],
                  "tarpit_config": {
                    "headers": {
                      "Content-Type": "text/html"
                    },
                    "timeout": 120000000000,
                    "bytes_per_second": 30,
                    "code": 200
                  }
                },
                {
                  "handler": "static_response",
                  "body": "Hello World"
                }
              ]
            }
          ]
        }
      }
    }
  }
}

Validation Rules

The following validation rules are enforced:
  • raw_responder must be one of: block, custom, drop, garbage, ratelimit, redirect, tarpit
  • When using custom: message field is required
  • When using redirect: url field is required
  • ranges must contain valid CIDR notation or predefined keys
  • whitelist only accepts IP addresses (not ranges)
  • tarpit_config.timeout must be greater than 0
  • tarpit_config.bytes_per_second must be greater than 10

Source Code Reference

The JSON configuration is handled by:
  • config.go:162-220 (UnmarshalJSON function)
  • config.go:223-254 (Validate function)
  • plugin.go:74-113 (Defender struct definition)

Build docs developers (and LLMs) love