Hardens Cloudflare Workers with rate limiting, observability, and middleware patterns.
Install
mkdir -p .claude/skills/cloudflare-workers-bot-scan-defense && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/14357" && unzip -o skill.zip -d .claude/skills/cloudflare-workers-bot-scan-defense && rm skill.zipInstalls to .claude/skills/cloudflare-workers-bot-scan-defense
Activation
This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.
Make a Cloudflare Workers app resilient to bot scans that arrive within minutes of HTTPS publication via CT Log enumeration. Use when deploying a new Worker (especially with auth or paid bindings), when budget/cost is a concern, or when you want to detect "/.env" / "/admin" / "/wp-login.php" / "/.git/config" probing. Covers the mental model (CT Log → bot scan → which paths actually cost you money), the edge-cache absorption that makes most scans free, the narrow set of unauthenticated routes that do need rate limiting (auth `begin`/`verify`), the exact `wrangler.jsonc` `observability` + `ratelimits` config (plus the wrangler 3.x `unsafe.bindings` fallback for projects still on v3), the IP-keyed Hono middleware pattern (with the fail-open variant), the verification flow via `wrangler versions view` + Workers Observability — including the trap that v3's `versions view` renders neither the `unsafe` ratelimit binding nor `observability`, and a credential-free verification path for sandboxed agents / keyless CI — and the documented eventual-consistency caveat that makes synthetic burst tests look like the limiter is broken.Key capabilities
- →Configure Workers Observability in `wrangler.jsonc`
- →Add Workers Rate Limit bindings to `wrangler.jsonc`
- →Implement IP-keyed Hono middleware for rate limiting
- →Verify rate limit configuration using `wrangler versions view`
- →Protect unauthenticated routes that perform CPU work
- →Audit existing Workers for exposed surfaces
How it works
This skill configures Cloudflare Workers to defend against bot scans by enabling observability and adding rate limit bindings and middleware to filter unwanted traffic to sensitive routes.
Inputs & outputs
When to use cloudflare-workers-bot-scan-defense
- →Enable rate limiting on auth routes
- →Configure observability in wrangler
- →Protect admin paths from probes
About this skill
Cloudflare Workers Bot Scan Defense
The day you point HTTPS at a domain — even a *.workers.dev subdomain you haven't told anyone about — your hostname appears in Certificate Transparency Logs, and within minutes scanner bots start probing /.env, /.git/config, /admin, /wp-login.php, /index.php.bak, /.DS_Store, and dozens more. Family-only apps get scanned. Staging gets scanned. Internal tools get scanned. Even sites with no UI link anywhere get scanned.
The bots are cheap to run and free to scale, so they don't care if you're a Fortune 500 or a hobby project — they just throw the wordlist and harvest whatever responds.
This skill captures the mental model, the small set of changes that actually matter on Cloudflare Workers, and the verification flow.
When to use this skill
- Deploying a Worker that will be reachable on HTTPS (with
*.workers.devor custom domain) — even if the URL is private - The Worker has unauthenticated routes that do CPU work (auth
begin/verify, magic-link issuance, signup, captcha, public webhooks) - Cost is a concern: paid plan with Workers AI / D1 reads / outbound subrequests, or you're near a free-tier ceiling
- You don't currently have observability — i.e., you couldn't answer "how many bot probes hit my Worker last night?" right now
- Auditing an existing Worker's exposed surface to decide what to harden first
The exclusions below apply specifically to the rate-limit binding + middleware portion of this skill. Workers Observability (the first artefact in "The minimum viable defense" below) is universally beneficial and should still be enabled even in the cases listed here — it costs nothing and gives you visibility regardless of how requests are gated.
Do not apply the rate-limit binding portion for:
- A Worker that returns hard 401/403 with no DB hit on every unauthenticated route (you're already fine — bots can't drain you)
- A Worker fronted by Cloudflare Access / IP allowlist where every route is already gated above the Worker layer (Access returns 302/403 before the Worker is invoked, so there's nothing left to rate-limit). Caveat: a custom domain protected by Access does not automatically protect the
<worker-name>.<account>.workers.devURL — that endpoint stays open by default and bypasses your Access policy. Setworkers_dev: falseinwrangler.jsoncto close it (recommended), or attach a separate Access application targeting the*.workers.devhostname (Dashboard → Zero Trust → Access → Applications → Add → Self-hosted, with the workers.dev hostname). Verify withcurl -I https://<worker-name>.<account>.workers.dev/after deploy — expect a 302 redirect or 403. - DDoS-grade attacks (you need WAF / Cloudflare Pro+ rules, not just a Worker binding)
- Application-level brute force (e.g., trying credentials against a known username) — that needs per-account lockout, a different design layer (no dedicated skill in this repo yet), not just per-IP rate limit
The mental model — what bots actually drain
Most bot scans target paths that don't exist in your app: /.env, /.git/config, /wp-admin/, etc. On a Cloudflare Workers + SPA setup with not_found_handling: "single-page-application", those paths fall through to the SPA fallback (index.html). After the first Worker invocation for each unique URL, the Cloudflare edge caches the response and serves subsequent requests with cf-cache-status: HIT — the Worker is not re-invoked. Bot scanners hammer the same wordlist URLs, so the long tail is absorbed by edge cache; only the first request per unique path costs you a Worker invocation. D1 isn't touched (the SPA fallback path doesn't touch DB). CPU billing for the cached path stops. You're already fine for the wordlist 99% of the time.
This holds regardless of run_worker_first: true|false. With run_worker_first: true, the first request per path invokes the Worker, which falls through to c.env.ASSETS.fetch(c.req.raw) for unknown paths; the response is cacheable and the edge memoizes it. With run_worker_first: false, the asset binding serves directly without Worker invocation. Either way, repeat scans hit cache.
What's actually expensive is the small set of unauthenticated routes that do real work:
| Route | Cost per call | Bot drain risk |
|---|---|---|
POST /api/auth/login/begin (WebAuthn challenge) | crypto + JWT sign + cookie | High — challenge generation is CPU |
POST /api/auth/register/begin | DB read + crypto | High if registration is open |
POST /api/auth/login/verify | DB read + crypto | High — D1 read fires before validation can short-circuit |
GET /health | constant body | Low — cheap and harmless |
/api/* with session middleware | nothing if no cookie (just 401) | Low — no D1 touch on missing cookie |
The defense you actually need is a small one: observability everywhere, rate limit on the 3-5 routes that do CPU work pre-auth. Don't bother adding rate limit to every /api/* endpoint — sessionMiddleware + missing cookie already returns 401 in microseconds without a DB hit.
Audit your attack surface in 2 minutes
Before you change anything, probe what's actually exposed. The output tells you which paths are edge-cached (free) vs. which paths reach the Worker (potentially expensive).
BASE=https://your-app.example.workers.dev
for path in "/" "/.env" "/.git/config" "/admin" "/wp-login.php" "/.DS_Store" \
"/api" "/api/cats" "/api/auth/me" "/api/auth/login/begin" "/health" "/robots.txt"; do
printf '%-25s ' "$path"
curl -skI --max-time 8 "$BASE$path" \
| grep -iE '^(HTTP/|cf-cache-status|content-type)' \
| tr '\n' ' '
echo
done
What to look for:
cf-cache-status: HITon bogus paths (/.env,/admin, ...) → edge is absorbing them, no Worker invocation. You don't need to do anything for these.HTTP/2 401on/api/*withapplication/json→ session middleware is short-circuiting, good. Verify no DB query happens (readworker/middleware/session.ts).HTTP/2 200on a public unauthenticated POST endpoint (e.g.,/api/auth/login/begin) → this is your protect-with-rate-limit target.HTTP/2 500on anything random → suspicious. Theapp.onErrorhandler should return a generic{error:{type:"internal"}}500 — never a stack trace. Fix this before adding rate limit.
Full probing recipe in references/attack-surface-audit.md.
The minimum viable defense
Three artefacts. None requires a paid plan.
1. Enable Workers Observability
Add to wrangler.jsonc:
{
"observability": {
"enabled": true,
"head_sampling_rate": 1
}
}
head_sampling_rate: 1 = 100%. For a low-traffic family/internal app this is fine; for a high-traffic public app drop to 0.1 or 0.01 to control cost. New Workers ship with Observability enabled by default (verified 2026-08-07), so on a fresh deploy this block is confirmation rather than activation — keep it explicit anyway: pre-existing Workers, or ones where it was switched off, otherwise sit with an empty dataset for your script, and you literally cannot see scan traffic.
2. Add a Workers Rate Limit binding
{
"ratelimits": [
{
"name": "AUTH_RATE_LIMITER",
"namespace_id": "1001",
"simple": {
"limit": 30,
"period": 60
}
}
]
}
namespace_id is an account-unique integer string (any number you pick). simple.period must be 10 or 60 — other values fail config validation. Two bindings sharing the same namespace_id share counters, which is intentional if you want "one rate limit across multiple Workers".
Wrangler 3.x fallback (no top-level ratelimits key)
The top-level ratelimits key above requires wrangler 4.36.0+. On a project still on wrangler 3.x, that key is rejected — but the same binding is available through the unsafe.bindings escape hatch with type: "ratelimit". The runtime binding is identical (env.AUTH_RATE_LIMITER.limit(...)); only the config shape differs:
{
// wrangler 3.x: top-level `ratelimits` is unsupported — use the unsafe form.
"unsafe": {
"bindings": [
{
"name": "AUTH_RATE_LIMITER",
"type": "ratelimit",
"namespace_id": "1001",
"simple": { "limit": 30, "period": 60 }
}
]
}
}
This is verified working on [email protected]. Note the cost: unsafe bindings are not validated by wrangler at config-parse time, and (see "Verify after deploy") wrangler 3.x's versions view does not render them — so a typo here fails silently. Prefer upgrading to 4.36+ and the top-level form when you can; use this only when the upgrade is out of scope. Pair it with the fail-open middleware (below) so a missing/misnamed binding degrades to "no rate limit" rather than locking every user out.
RateLimit is a global type from @cloudflare/workers-types — no import needed in a worker tsconfig. Add it to your Bindings:
type Bindings = {
AUTH_RATE_LIMITER: RateLimit;
// ... other bindings
};
3. Apply per-route in Hono via middleware
// worker/middleware/rate-limit.ts
import { createMiddleware } from "hono/factory";
import type { Env } from "../types";
export const authRateLimit = createMiddleware<Env>(async (c, next) => {
const ip = c.req.header("CF-Connecting-IP") ?? "unknown";
const { success } = await c.env.AUTH_RATE_LIMITER.limit({ key: ip });
if (!success) {
return c.json({ error: { type: "rate_limited", message: "Too many requests" } }, 429);
}
await next();
});
// worker/routes/auth.ts — apply only to the unauthenticated CPU-spending routes
export const authRoutes = new Hono<Env>()
.post("/register/begin", authRateLimit, async (c) => { /* ... */ })
.post("/register/verify", authRateLimit, async (c) => { /* ...
---
*Content truncated.*
When not to use it
- →When a Worker returns hard 401/403 with no DB hit on every unauthenticated route
- →When a Worker is fronted by Cloudflare Access or IP allowlist
- →When dealing with DDoS-grade attacks requiring WAF/Cloudflare Pro+ rules
Limitations
- →The skill does not cover DDoS-grade attacks.
- →The skill does not implement application-level brute force protection.
- →The skill does not integrate Captcha/Turnstile.
How it compares
This skill provides specific configurations and middleware patterns for Cloudflare Workers to absorb bot scans at the edge, unlike general security measures that may not be optimized for Workers' architecture.
Compared to similar skills
cloudflare-workers-bot-scan-defense side by side with the closest alternatives in the catalog.
| Skill | Installs | Updated | Safety | Difficulty |
|---|---|---|---|---|
| cloudflare-workers-bot-scan-defense (this skill) | 0 | 2mo | Caution | Intermediate |
| security-header-generator | 5 | 9mo | Caution | Intermediate |
| protocol-reverse-engineering | 9 | 6mo | Review | Advanced |
| backend-security-coder | 24 | 4mo | No flags | Intermediate |
Try saying
Example prompts that trigger this skill in your AI assistant.
You might also like
security-header-generator
Dexploarer
Generates security HTTP headers (CSP, HSTS, CORS, etc.) for web applications to prevent common attacks. Use when user asks to "add security headers", "setup CSP", "configure CORS", "secure headers", or "HSTS setup".
protocol-reverse-engineering
wshobson
Master network protocol reverse engineering including packet analysis, protocol dissection, and custom protocol documentation. Use when analyzing network traffic, understanding proprietary protocols, or debugging network communication.
backend-security-coder
sickn33
Expert in secure backend coding practices specializing in input validation, authentication, and API security. Use PROACTIVELY for backend security implementations or security code reviews.
api-security-best-practices
davila7
Implement secure API design patterns including authentication, authorization, input validation, rate limiting, and protection against common API vulnerabilities
equilateral-agents
Equilateral-AI
22 production-ready AI agents with database-driven orchestration for security reviews, code quality analysis, deployment validation, infrastructure checks, and compliance. Auto-activates for security concerns, deployment tasks, code reviews, quality checks, and compliance questions. Includes upgrade paths to enterprise features (GDPR, HIPAA, multi-account AWS, ML-based optimization).
springboot-security
affaan-m
Spring Security best practices for authn/authz, validation, CSRF, secrets, headers, rate limiting, and dependency security in Java Spring Boot services.