skills/cloudflare-worker-dev/SKILL.md
Cloudflare Workers, KV, Durable Objects, and edge computing development. Use for serverless APIs, caching, rate limiting, real-time features. Activate on "Workers", "KV", "Durable Objects", "wrangler", "edge function", "Cloudflare". NOT for Cloudflare Pages configuration (use deployment docs), DNS management, or general CDN settings.
npx skillsauth add curiositech/windags-skills cloudflare-worker-devInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Build high-performance edge APIs with Workers, KV for caching, and Durable Objects for real-time coordination.
Need real-time state consistency?
├── YES → Use Durable Objects
│ ├── Chat/collaboration → WebSocket pattern
│ ├── Counters/queues → Atomic operations
│ └── Session management → Per-user Objects
└── NO → Continue to storage decision
├── Need fast reads, eventual consistency OK?
│ ├── YES → Use KV
│ │ ├── Cache TTL < 1 hour → expirationTtl: 3600
│ │ ├── Config/sessions → expirationTtl: 86400
│ │ └── Long-term cache → expirationTtl: 604800
│ └── NO → Use external DB (D1/Postgres)
└── File storage needed?
└── YES → Use R2 (images, documents)
Geographic scope?
├── City-level (10km radius) → 4-character geohash
├── Metro area (50km radius) → 3-character geohash
├── Regional (100km radius) → 2-character geohash
└── Country-level → Use country code instead
Cache hit rate priority?
├── HIGH → Use 2-3 character geohash (broader cache sharing)
└── PRECISION → Use 4-5 character geohash (location accuracy)
Rate limit scope?
├── Per IP → Use CF-Connecting-IP header
├── Per user → Use auth token/user ID
├── Per API key → Use Authorization header
└── Global → Use fixed key
Time window?
├── Burst protection (< 1 min) → Use in-memory counter
├── Short term (1-60 min) → Use KV with TTL
└── Long term (> 1 hour) → Use external storage
Symptoms: Worker returns 1102 error code, logs show "CPU time limit exceeded" Detection Rule: If logs contain "exceeded CPU time" or status 1102 responses Root Cause: Synchronous operations blocking event loop (JSON parsing large payloads, complex loops) Fix:
console.time() to find bottlenecksresponse.json() → ReadableStreamawait scheduler.wait(0) every 1000 iterationsSymptoms: Stale data returned immediately after writes, cache "flapping" between values Detection Rule: If read-after-write returns old value or cache hit rate drops unexpectedly Root Cause: Reading KV immediately after write hits different edge nodes Fix:
await kv.put(key, data); return data;X-Cache-Status header to debug cache behaviorSymptoms: Users get inconsistent rate limit responses, some bypass limits under load Detection Rule: If rate limit counters drift from expected values or users report sporadic 429s Root Cause: Multiple concurrent requests increment counter simultaneously Fix:
Symptoms: Browser requests work in dev but fail in production, OPTIONS returns 404 Detection Rule: If seeing "CORS policy" errors in browser console or 404s on OPTIONS requests Root Cause: Missing OPTIONS handler in route logic Fix:
Symptoms: Cache updates don't happen, cleanup tasks never run, inconsistent state
Detection Rule: If cache miss rates increase or background metrics stop updating
Root Cause: Using await instead of ctx.waitUntil() for background work
Fix:
ctx.waitUntil()Scenario: API needs to cache meeting data by geographic region with sub-second response times.
Step 1: Service Selection Decision
Step 2: Implementation
async function getMeetings(lat: number, lng: number, env: Env, ctx: ExecutionContext) {
// 3-char geohash for ~150km cells (metro coverage)
const geohash = Geohash.encode(lat, lng, 3);
const cacheKey = `meetings:${geohash}`;
// Check cache first
const cached = await env.MEETING_CACHE.get(cacheKey, 'json');
if (cached) {
return { data: cached, source: 'cache', geohash };
}
// Cache miss - fetch fresh data
const meetings = await fetchFromAPI(lat, lng);
// Background cache update (non-blocking)
ctx.waitUntil(
env.MEETING_CACHE.put(cacheKey, JSON.stringify(meetings), {
expirationTtl: 3600, // 1 hour TTL for meeting data
metadata: { cachedAt: Date.now(), coords: `${lat},${lng}` }
})
);
return { data: meetings, source: 'api', geohash };
}
Expert vs Novice Decision Points:
Step 3: Rate Limiting Integration
export default {
async fetch(request: Request, env: Env, ctx: ExecutionContext) {
const ip = request.headers.get('CF-Connecting-IP') || 'unknown';
// Check rate limit first (fail fast)
const rateCheck = await checkRateLimit(ip, env, { maxRequests: 100, windowSeconds: 3600 });
if (!rateCheck.allowed) {
return new Response('Rate limited', { status: 429 });
}
// Process request
const url = new URL(request.url);
const lat = parseFloat(url.searchParams.get('lat') || '0');
const lng = parseFloat(url.searchParams.get('lng') || '0');
const result = await getMeetings(lat, lng, env, ctx);
return new Response(JSON.stringify(result.data), {
headers: {
'Content-Type': 'application/json',
'X-Cache': result.source,
'X-Geohash': result.geohash,
'X-RateLimit-Remaining': rateCheck.remaining.toString()
}
});
}
};
Deployment Readiness Checklist:
wrangler secret put, no hardcoded values in wrangler.toml or codewrangler tail)ctx.waitUntil(), no blocking operations in response pathThis skill should NOT be used for:
static-site-deployment skill insteadcloudflare-dns-management skill insteadcloudflare-cdn-config skill insteadai-integration skill insteadbatch-processing skill with external storagebackground-job-processing skill insteaddatabase-optimization skill with dedicated DBemail-service-integration skill insteadDelegate when:
tools
Building resilient distributed systems with circuit breakers, retries with full-jitter exponential backoff, retry budgets (per-request 3-attempt + per-client 10% ratio per Google SRE), deadline propagation, and the cascading-failure math (4 layers × 3 retries = 64x amplification). Grounded in Resilience4j, Microsoft Cloud Patterns, AWS Architecture Blog (Marc Brooker), and Google SRE Book.
testing
Designing HTTP cache headers that work correctly across browsers, CDNs, and shared proxies — `Cache-Control` directives per RFC 9111, `stale-while-revalidate` and `stale-if-error` per RFC 5861, the Vary header for varying responses, and surrogate keys for tag-based purging. Grounded in IETF RFCs and Cloudflare/Fastly docs.
development
Use when designing or fixing a Content Security Policy on a real site, choosing between nonce-based and hash-based CSP, adding strict-dynamic, debugging "Refused to execute inline script" errors, deploying CSP in report-only mode first, configuring report-to / report-uri, or auditing an existing policy for unsafe-inline / unsafe-eval / wildcards. Triggers: "CSP blocks legitimate inline script", strict-dynamic, nonce-{RANDOM}, sha256-{HASH}, object-src none, base-uri none, frame-ancestors, Trusted Types, X-Content-Security-Policy obsolete, report-only vs enforced. NOT for general HTTP security headers (HSTS, COOP/COEP), Trusted Types deep dive, CORS configuration, or building a WAF.
tools
Choosing and operating an HTTP API versioning strategy that doesn't break clients — Stripe's date-based pinned versions, the Deprecation/Sunset header pair (RFC 9745 + RFC 8594), URI vs header vs media-type approaches, and the version-transformer pattern. Grounded in Stripe's published architecture and IETF RFCs.