Caddy WAF (Regex Rules, IP and DNS filtering, Rate Limiting, GeoIP, Tor, Anomaly Detection)
Caddy WAF (Regex Rules, IP and DNS filtering, Rate Limiting, GeoIP, Tor, Anomaly Detection)
A Web Application Firewall middleware for the Caddy web server, written in Go.
http.handlers.waf — registered in Caddy's package registry, so the module is selectable on the download pagegithub.com/fabriziosalmi/caddy-wafv0.4.14 (see caddywaf.go — const wafVersion)caddy-waf is an HTTP handler middleware that inspects requests and responses across four well-defined phases, applies a regular-expression rule set with anomaly scoring, enforces IP/DNS/ASN/country blacklists and whitelists, performs token-bucket-style rate limiting, and exposes a JSON metrics endpoint.
The middleware is implemented as a single Caddy module registered under the ID http.handlers.waf. It can be configured through the Caddyfile or directly via JSON.
| Capability | Implementation |
|---|---|
| Regex rule engine | Go regexp package (RE2, linear-time guarantee). Compiled patterns are cached per rule ID. |
| Multi-phase inspection | Phase 1 (request headers and pre-request checks), Phase 2 (request body), Phase 3 (response headers), Phase 4 (response body). |
| Anomaly scoring | Each rule contributes its score to a per-request total; requests are blocked when the total reaches anomaly_threshold. |
| Explicit actions | Rule mode of block or log. block short-circuits the request; log records the match and continues. |
| IP blacklist | Plain IPs and CIDR ranges (IPv4 and IPv6) stored in a prefix trie (go-iptrie). |
| DNS blacklist | Exact-match (case-insensitive) host lookup. |
| GeoIP country block / whitelist | MaxMind GeoLite2 Country MMDB. |
| ASN block | MaxMind GeoLite2 ASN MMDB. |
| Tor exit-node block | Periodic fetch from https://check.torproject.org/torbulkexitlist. |
| Rate limiting | Per-IP, sliding-window, optional per-path matching with regex patterns. |
| Custom block responses | Per-status-code response with custom Content-Type, headers, and body (inline or from file). |
| Sensitive data redaction | Optional redaction of sensitive query parameters and log fields. |
| Hot reload | fsnotify watchers on rule files, IP blacklist, and DNS blacklist. |
| Metrics endpoint | JSON document exposed at the configured metrics_endpoint path. |
| Asynchronous logging | Buffered log channel with synchronous fallback when the buffer is full. |
regexp (RE2). No catastrophic backtracking.atomic.Int64 stored in a sync.Map.io.LimitReader (max_request_body_size, default 10 MiB) and restored with io.MultiReader so downstream handlers still see the full body.max_response_body_size (default 10 MiB). Beyond that — or as soon as the upstream flushes — the WAF releases what it holds and streams the rest, so memory never scales with the response size.unsafe.String to avoid an allocation per request.geoip_fail_open controls whether a GeoIP lookup failure blocks the request or allows it through.ServeHTTP installs a deferred recovery that returns 500 Internal Server Error on panic.The fastest path to a working build:
curl -fsSL -H "Pragma: no-cache" \
https://raw.githubusercontent.com/fabriziosalmi/caddy-waf/refs/heads/main/install.sh | bash
The script ensures Go and xcaddy are installed, clones the repository, downloads the GeoLite2 database, builds Caddy with the caddy-waf module, and starts the server.
A representative provisioning log:
…
go.mod declares go 1.25.1, propagated from caddy/v2 which requires it)github.com/caddyserver/caddy/v2 v2.11.4)xcaddy for building Caddy with pluginsgo install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
xcaddy build --with github.com/fabriziosalmi/caddy-waf
./caddy list-modules | grep waf # expect: http.handlers.waf
curl -fsSL -H "Pragma: no-cache" \
https://raw.githubusercontent.com/fabriziosalmi/caddy-waf/refs/heads/main/install.sh | bash
git clone https://github.com/fabriziosalmi/caddy-waf.git
cd caddy-waf
go mod tidy
wget https://git.io/GeoLite2-Country.mmdb # optional, only for GeoIP
xcaddy build --with github.com/fabriziosalmi/caddy-waf=./
./caddy fmt --overwrite
./caddy run
caddy add-package[!IMPORTANT] Prefer
xcaddyor the container image. Caddy's maintainers have proposed movingadd-package,remove-packageandupgradeout of Caddy's core to discourage their use (caddyserver/caddy#7010): the commands call Caddy's shared build server, and using them in CI/CD is an anti-pattern. Raised for this project in #138 by a Caddy maintainer.The command works today and the module remains registered, so this section stays accurate. Treat it as a convenience for one-off, hand-operated installs — not as the way to build or deploy caddy-waf.
The module is registered in Caddy's package registry, so an existing Caddy v2.7+ binary can pull it in without a Go toolchain:
caddy add-package github.com/fabriziosalmi/caddy-waf
caddy list-modules | grep waf # expect: http.handlers.waf
It is also selectable on caddyserver.com/download. See docs/add-package-guide.md for version pinning, removal, and when to prefer xcaddy instead.
Images are published to GitHub Container Registry on every release tag, for linux/amd64 and linux/arm64:
docker pull ghcr.io/fabriziosalmi/caddy-waf:0.4.14
docker run --rm -p 8080:8080 ghcr.io/fabriziosalmi/caddy-waf:0.4.14
Tags are 0.4.14, 0.4 and latest — note there is no v prefix, unlike the Go module version. Pin an exact version in anything you deploy. See docs/docker.md for volumes, Compose, and hot reload.
…
A fully annotated example is provided in Caddyfile and caddyfile.example.
fabriziosalmi.github.io/caddy-waf — the same pages as below, with full-text search and cross-linking. The Markdown under docs/ remains the source and stays readable on GitHub.
| Document | Topic |
|---|---|
docs/introduction.md |
What the middleware does and where it sits in the request pipeline. |
docs/installation.md |
All installation methods. |
docs/configuration.md |
Caddyfile and JSON directives, request lifecycle, blocking precedence. |
docs/rules.md |
rules.json schema, target identifiers, regex semantics. |
docs/blacklists.md |
IP and DNS blacklist file formats. |
docs/ratelimit.md |
Rate-limit block, path matching, behavior. |
docs/geoblocking.md |
Country block / whitelist, ASN block, fallback behavior. |
docs/attacks.md |
Attack categories addressed by the bundled rule sets. |
docs/dynamicupdates.md |
File watchers, reload semantics, scope of each reload. |
docs/metrics.md |
/waf_metrics JSON schema. |
docs/prometheus.md |
Bridging the JSON metrics to Prometheus and Grafana. |
docs/caddy-waf-elk.md |
Shipping logs to an ELK stack with Filebeat. |
docs/scripts.md |
Helper Python scripts for rule and blacklist generation. |
docs/testing.md |
Running the bundled test.py suite. |
docs/docker.md |
Building and running with Docker / Docker Compose. |
docs/add-package-guide.md |
Installing with caddy add-package. |
docs/caddytest.md |
The caddytest.py traffic-generation tool. |
…
make test # go test -v ./...
make it # go test -v ./... -tags=it (integration)
make lint # golangci-lint run
make test-integration # runs test.py inside a python:3.9-slim container
The repository also ships Python suites covering offensive payloads (test.py), traffic generation (caddytest.py), and benchmarking (benchmark.py). See docs/testing.md and docs/caddytest.md.
Running caddy-waf in production? I offer paid support, custom rule development, and security consulting — WAF tuning, hardening, TLS automation, and cloud detection & alerting. Reach out: [email protected].
Pull requests are welcome. The project values:
rules/ using the documented JSON schema.docs/configuration.md and the relevant topic page).See CONTRIBUTING.md for the workflow and CODE_OF_CONDUCT.md.
Please do not open a public issue for a vulnerability. Use private vulnerability reporting — it is enabled on this repository, so it works for anyone without special permissions — or email [email protected]. Full policy in SECURITY.md.
Only the latest release receives security fixes; there are no backport branches. Published advisories are listed under Security → Advisories.
Upgrade note: v0.3.3 and earlier are affected by GHSA-gfj3-cmff-q8wh, a high-severity unauthenticated denial of service (unbounded response buffering, CVSS 7.5). Fixed in v0.3.4.
AGPL-3.0. See LICENSE.
No open issues yet, or sync has not completed.