Skip to content

Caddy Warden: Getting Started ​

Caddy Warden (github.com/routewarden/caddy-warden) is the official Caddy v2 security module from RouteWarden. It brings high-performance sensitive path defense, anti-evasion normalization, IP allowlisting, and active deception defenses to Caddy web servers.


Key Capabilities ​

  • Automatic Sensitive Asset Shielding: Blocks .env, .git, .aws, .ssh, .sql, database dumps, and server manifests out-of-the-box (enable_default_patterns).
  • Anti-Evasion Engine: Normalizes multiple URL encodings (%252e%252e), semicolon matrix parameters (/;param/.env), Windows backslashes (\), and null bytes (%00) before pattern matching.
  • IP & CIDR Allowlisting: Exempt trusted corporate subnets, office IPs, or VPNs (allowed_ips) using client IP detection or upstream proxy headers (X-Forwarded-For, X-Real-IP).
  • Multi-Action Defense Engine: Respond with JSON errors, branded HTML, Cloudflare Turnstile/hCaptcha challenges, silent TCP resets (silent_drop), or bot-neutralizing Gzip Bombs (gzip_bomb).

Installation & Building Caddy ​

Caddy uses xcaddy to compile custom builds with plugins:

# Install xcaddy if you haven't alreadygo install github.com/caddyserver/xcaddy/cmd/xcaddy@latest# Build Caddy with caddy-wardenxcaddy build \  --with github.com/routewarden/caddy-warden@v1.4.1

Using Dockerfile ​

# DockerfileFROM caddy:2-builder AS builderRUN xcaddy build \    --with github.com/routewarden/caddy-warden@v1.4.1FROM caddy:2-alpineCOPY --from=builder /usr/bin/caddy /usr/bin/caddy

Using Docker Compose ​

Run Caddy with RouteWarden alongside your upstream web services using a dedicated Dockerfile:

services:  caddy:    build:      context: .      dockerfile: Dockerfile    ports:      - "80:80"      - "443:443"    volumes:      - ./Caddyfile:/etc/caddy/Caddyfile:ro      - caddy_data:/data      - caddy_config:/config    depends_on:      - webapp    restart: unless-stopped  webapp:    image: nginx:alpine    restart: unless-stoppedvolumes:  caddy_data:  caddy_config:

Configuration ​

You can define RouteWarden security rules using routewarden.json (Recommended Universal Schema) as your single source of truth, directly in your Caddyfile, or via Caddy's dynamic JSON API.

How routewarden.json Works with Caddy ​

routewarden.json acts as a portable security policy with IDE autocompletion and CI/CD validation. Its properties correspond directly to Caddy's directive blocks and JSON handler objects:

  • "methods" ➔ Caddyfile methods GET POST / JSON "methods": ["GET", "POST"]
  • "allowedIps" ➔ Caddyfile allowed_ips ... / JSON "allowed_ips": [...]
  • "blockPatterns" ➔ Caddyfile block_patterns ... / JSON "block_patterns": [...]
  • "response" ➔ Caddyfile response { mode ... } / JSON "response": { ... }
{  "$schema": "https://routewarden.github.io/schema.json",  "enabled": true,  "enableDefaultPatterns": true,  "enableDefaultAllowPatterns": true,  "methods": ["GET", "POST"],  "allowedIps": ["10.0.0.0/8", "192.168.1.0/24"],  "response": {    "mode": "json",    "statusCode": 403,    "body": "{\"error\":\"Access Denied\",\"security\":\"RouteWarden Shield\"}"  }}

Using routewarden.json Directly via Generate Pipeline ​

If you maintain routewarden.json as your single source of truth across Git repositories or multi-gateway environments, use the RouteWarden CLI (rwarden) to validate rules offline and compile directly into Caddyfile directive blocks during your deployment pipeline:

# 1. Validate schema compliance, regex patterns, and CIDRsrwarden validate --config routewarden.json# 2. Compile directly into Caddyfile directive blockrwarden generate --target caddy --config routewarden.json > Caddyfile

Directive Ordering ​

In Caddy, custom HTTP handler modules must be ordered in the middleware chain. Add order routewarden before basicauth or order routewarden before reverse_proxy inside your Caddyfile global options block:

{    order routewarden before basicauth}

Option 2: Caddyfile Syntax ​

routewarden {    enabled <true|false>    enable_default_patterns <true|false>    enable_default_allow_patterns <true|false>    check_query <true|false>    block_patterns <regex...>    allow_patterns <regex...>    allowed_ips <ip_or_cidr...>    response {        mode <json|html|text|xml|redirect|captcha|silent_drop|gzip_bomb|tarpit|fake_success|ratelimit|proxy|infinite_stream>        status_code <int>        body <string>        redirect_url <url>        proxy_url <url>        gzip_bomb_mb <int>        retry_after_seconds <int>        tarpit_delay_ms <int>        stream_size_mb <int>        captcha {            provider <turnstile|hcaptcha|recaptcha>            site_key <key>        }    }}

Examples ​

1. Basic Production Shield (JSON 404) ​

Shield all sensitive paths and return a sterile JSON 404 response:

{    order routewarden before reverse_proxy}example.com {    routewarden {        enable_default_patterns true        response {            mode json            status_code 404            body "{\"error\":\"Not Found\"}"        }    }    reverse_proxy localhost:8080}

2. IP Whitelisting with Safe Admin Exceptions ​

Allow corporate VPN (10.0.0.0/8) and office IP (192.168.1.100) to access administrative endpoints while blocking external crawlers:

{    order routewarden before reverse_proxy}app.example.com {    routewarden {        enable_default_patterns true        block_patterns "(?i)^/admin(/.*)?$" "(?i)^/metrics$"        allow_patterns "(?i)^/admin/health$"        allowed_ips "10.0.0.0/8" "192.168.1.100"        response {            mode json            status_code 403            body "{\"error\":\"Access Denied: Internal Network Only\"}"        }    }    reverse_proxy backend:3000}

3. Active Defense: Gzip Bomb Decompression Trap ​

When automated scrapers scan for .env or WordPress admin endpoints, send an active defense gzip bomb stream that expands ~1000× in client RAM:

{    order routewarden before reverse_proxy}honeypot.example.com {    routewarden {        enable_default_patterns true        block_patterns "(?i)^/wp-login\.php$" "(?i)^/xmlrpc\.php$"        response {            mode gzip_bomb            status_code 200            gzip_bomb_mb 10        }    }    reverse_proxy backend:80}

4. Interactive Captcha Verification ​

Challenge visitors accessing sensitive URLs using Cloudflare Turnstile:

{    order routewarden before reverse_proxy}portal.example.com {    routewarden {        block_patterns "(?i)^/portal/sensitive(/.*)?$"        response {            mode captcha            captcha {                provider turnstile                site_key "0x4AAAAAAAxxyyzz"            }        }    }    reverse_proxy backend:8080}

JSON Configuration (Caddy Native API) ​

If you configure Caddy via its native JSON API:

{  "apps": {    "http": {      "servers": {        "srv0": {          "listen": [":443"],          "routes": [            {              "handle": [                {                  "handler": "routewarden",                  "enabled": true,                  "enable_default_patterns": true,                  "allowed_ips": ["10.0.0.0/8"],                  "response": {                    "mode": "json",                    "status_code": 404,                    "body": "{\"error\":\"Not Found\"}"                  }                },                {                  "handler": "reverse_proxy",                  "upstreams": [                    { "dial": "localhost:8080" }                  ]                }              ]            }          ]        }      }    }  }}

Released under the MIT License.