Automated DDoS mitigation for Cloudflare. Listens for Cloudflare DDoS webhook alerts, flips the zone into "Under Attack" mode, and signals a Cloudflare Worker to serve SHA-256 proof-of-work challenges to visitors until the attack ends.
Cloudflare ──webhook──▶ Deter Server ──API──▶ Cloudflare (set "under_attack")
│
▼
Workers KV (attack status)
│
▼
Deter Worker ◀──HTTP──▶ Browser (PoW challenge)
- Cloudflare sends a DDoS alert webhook to the server.
- The server verifies the HMAC signature, parses the payload, and calls the Cloudflare API to set the zone's security level to
under_attack. - It writes an
activestatus to Workers KV so the edge worker knows an attack is happening. - The worker reads KV on each request. If active, it serves a proof-of-work challenge page. The browser computes a SHA-256 hash with the required difficulty, submits it back, and gets an HMAC-signed cookie that bypasses the challenge for 1 hour.
- When the attack ends, Cloudflare sends another webhook. The server reverts the security level and sets KV to
inactive.
Duplicate webhooks for the same zone are skipped (in-memory idempotency). Webhook timestamps older than 5 minutes are rejected to prevent replay attacks.
Requires Go 1.26+.
go build -o deter-server ./cmd/deter-server
export CF_API_TOKEN="your-cf-api-token"
export CF_ACCOUNT_ID="your-account-id"
export TARGET_ZONE_ID="your-zone-id"
export KV_NAMESPACE_ID="your-kv-namespace-id"
export CF_WEBHOOK_SECRET="your-webhook-secret"
./deter-serverRequires Wrangler.
- Edit
worker/wrangler.tomlwith your account ID, zone ID, and KV namespace ID. - Deploy:
cd worker
npx wrangler deploy| Variable | Description | Default |
|---|---|---|
CF_API_TOKEN |
Cloudflare API token | required |
CF_ACCOUNT_ID |
Cloudflare account ID | required |
TARGET_ZONE_ID |
Zone ID to protect | required |
KV_NAMESPACE_ID |
KV namespace for attack status | required |
CF_WEBHOOK_SECRET |
HMAC secret for webhook signature verification | empty (verification skipped) |
LISTEN_ADDR |
Server listen address | :8080 |
DEFAULT_SEC_LEVEL |
Security level to revert to after attack | medium |
KV_KEY_PREFIX |
Prefix for KV keys | attack_status_zone_ |
WEBHOOK_TIMEOUT_SECONDS |
Timeout for webhook processing | 30 |
| Variable | Description | Default |
|---|---|---|
TARGET_ZONE_ID |
Zone ID (must match server config) | required |
KV_NAMESPACE |
KV namespace binding | required |
POW_DIFFICULTY |
Number of leading hex zeros required | 4 |
POW_SECRET |
HMAC secret for session cookies | deter-pow-secret |
Start the server with dummy credentials. Omit CF_WEBHOOK_SECRET to skip signature verification:
CF_API_TOKEN=dummy CF_ACCOUNT_ID=dummy TARGET_ZONE_ID=zone123 KV_NAMESPACE_ID=dummy go run ./cmd/deter-serverSimulate an attack starting:
curl -X POST http://localhost:8080/cloudflare-webhook \
-H "Content-Type: application/json" \
-d '{"alert_id":"a1","alert_type":"dos","zone_id":"zone123","attack_id":"atk1"}'Simulate an attack ending:
curl -X POST http://localhost:8080/cloudflare-webhook \
-H "Content-Type: application/json" \
-d '{"alert_id":"a2","alert_type":"dos","zone_id":"zone123","attack_id":"atk1","ended_at":"2026-03-25T12:00:00Z"}'To test with signature verification:
BODY='{"alert_id":"a1","alert_type":"dos","zone_id":"zone123","attack_id":"atk1"}'
TIMESTAMP=$(date +%s)
SIG=$(printf '%s.%s' "$TIMESTAMP" "$BODY" | openssl dgst -sha256 -hmac "testsecret" -hex | awk '{print $2}')
curl -X POST http://localhost:8080/cloudflare-webhook \
-H "Content-Type: application/json" \
-H "Cf-Webhook-Signature: t=$TIMESTAMP,v1=$SIG" \
-d "$BODY"For the worker, use npx wrangler dev inside worker/. Cloudflare API calls will fail with dummy tokens, but webhook parsing, signature verification, and idempotency all work locally.
| Method | Path | Description |
|---|---|---|
POST |
/cloudflare-webhook |
Receives Cloudflare DDoS alert webhooks |
GET |
/health |
Returns 200 OK |