home / skills / personamanagmentlayer / pcl / cloudflare-expert

cloudflare-expert skill

/stdlib/cloud/cloudflare-expert

This skill provides expert guidance on Cloudflare Workers, edge computing, and security services to optimize performance and protect applications.

npx playbooks add skill personamanagmentlayer/pcl --skill cloudflare-expert

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
13.4 KB
---
name: cloudflare-expert
version: 1.0.0
description: Expert-level Cloudflare Workers, CDN, edge computing, and security services
category: cloud
tags: [cloudflare, edge-computing, cdn, workers, waf]
allowed-tools:
  - Read
  - Write
  - Edit
  - Bash(wrangler:*)
---

# Cloudflare Expert

Expert guidance for Cloudflare Workers, edge computing, CDN optimization, and Cloudflare security services.

## Core Concepts

### Cloudflare Services
- Cloudflare Workers (serverless edge computing)
- CDN and caching
- DDoS protection
- Web Application Firewall (WAF)
- DNS management
- Load balancing
- Workers KV (key-value storage)
- Durable Objects

### Edge Computing
- Deploy code globally
- Reduce latency
- Process at the edge
- Distributed state
- Real-time applications

### Developer Tools
- Wrangler CLI
- Workers Playground
- Edge APIs
- Analytics and logs

## Cloudflare Workers

```javascript
// Basic Worker
export default {
  async fetch(request, env, ctx) {
    return new Response('Hello from Cloudflare Workers!', {
      headers: { 'Content-Type': 'text/plain' }
    });
  }
};

// Advanced routing
export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);

    // Route based on path
    switch (url.pathname) {
      case '/api/users':
        return handleUsers(request, env);
      case '/api/posts':
        return handlePosts(request, env);
      default:
        return new Response('Not Found', { status: 404 });
    }
  }
};

// API endpoint with JSON
async function handleUsers(request, env) {
  if (request.method === 'GET') {
    const users = await env.USERS_KV.get('users', { type: 'json' });
    return new Response(JSON.stringify(users), {
      headers: { 'Content-Type': 'application/json' }
    });
  }

  if (request.method === 'POST') {
    const body = await request.json();
    await env.USERS_KV.put('users', JSON.stringify(body));
    return new Response('Created', { status: 201 });
  }

  return new Response('Method Not Allowed', { status: 405 });
}
```

## Workers KV Storage

```javascript
// Workers KV operations
export default {
  async fetch(request, env, ctx) {
    // Write
    await env.MY_KV.put('key', 'value');

    // Write with metadata and expiration
    await env.MY_KV.put('key', 'value', {
      metadata: { userId: '123' },
      expirationTtl: 3600 // 1 hour
    });

    // Read
    const value = await env.MY_KV.get('key');

    // Read as JSON
    const jsonValue = await env.MY_KV.get('key', { type: 'json' });

    // Read with metadata
    const { value, metadata } = await env.MY_KV.getWithMetadata('key');

    // Delete
    await env.MY_KV.delete('key');

    // List keys
    const keys = await env.MY_KV.list({ prefix: 'user:' });

    return new Response(JSON.stringify({ value, keys }));
  }
};

// Caching pattern
class CachedAPI {
  constructor(kv) {
    this.kv = kv;
  }

  async get(key, fetcher, ttl = 3600) {
    // Try cache first
    const cached = await this.kv.get(key, { type: 'json' });
    if (cached) return cached;

    // Fetch and cache
    const data = await fetcher();
    await this.kv.put(key, JSON.stringify(data), {
      expirationTtl: ttl
    });

    return data;
  }
}

export default {
  async fetch(request, env, ctx) {
    const cache = new CachedAPI(env.MY_KV);

    const data = await cache.get('api:users', async () => {
      const response = await fetch('https://api.example.com/users');
      return response.json();
    }, 3600);

    return new Response(JSON.stringify(data));
  }
};
```

## Durable Objects

```javascript
// Durable Object for stateful logic
export class Counter {
  constructor(state, env) {
    this.state = state;
    this.value = 0;
  }

  async initialize() {
    this.value = await this.state.storage.get('value') || 0;
  }

  async fetch(request) {
    await this.initialize();

    const url = new URL(request.url);

    if (url.pathname === '/increment') {
      this.value++;
      await this.state.storage.put('value', this.value);
      return new Response(String(this.value));
    }

    if (url.pathname === '/decrement') {
      this.value--;
      await this.state.storage.put('value', this.value);
      return new Response(String(this.value));
    }

    return new Response(String(this.value));
  }
}

// WebSocket chat room with Durable Objects
export class ChatRoom {
  constructor(state, env) {
    this.state = state;
    this.sessions = [];
  }

  async fetch(request) {
    if (request.headers.get('Upgrade') !== 'websocket') {
      return new Response('Expected WebSocket', { status: 426 });
    }

    const pair = new WebSocketPair();
    const [client, server] = Object.values(pair);

    await this.handleSession(server);

    return new Response(null, {
      status: 101,
      webSocket: client
    });
  }

  async handleSession(websocket) {
    websocket.accept();
    this.sessions.push(websocket);

    websocket.addEventListener('message', async (msg) => {
      // Broadcast to all sessions
      for (const session of this.sessions) {
        try {
          session.send(msg.data);
        } catch (err) {
          // Remove closed sessions
          this.sessions = this.sessions.filter(s => s !== session);
        }
      }
    });
  }
}

// Worker that uses Durable Object
export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);
    const roomId = url.pathname.slice(1);

    // Get or create Durable Object
    const id = env.CHAT_ROOM.idFromName(roomId);
    const stub = env.CHAT_ROOM.get(id);

    return stub.fetch(request);
  }
};
```

## Request/Response Handling

```javascript
// CORS handling
function handleCORS(request) {
  const corsHeaders = {
    'Access-Control-Allow-Origin': '*',
    'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE',
    'Access-Control-Allow-Headers': 'Content-Type, Authorization'
  };

  // Handle preflight
  if (request.method === 'OPTIONS') {
    return new Response(null, { headers: corsHeaders });
  }

  return corsHeaders;
}

// Authentication middleware
async function authenticate(request, env) {
  const token = request.headers.get('Authorization')?.replace('Bearer ', '');

  if (!token) {
    return new Response('Unauthorized', { status: 401 });
  }

  // Verify token (example using JWT)
  try {
    const payload = await verifyJWT(token, env.JWT_SECRET);
    return payload;
  } catch (err) {
    return new Response('Invalid token', { status: 401 });
  }
}

// Rate limiting
class RateLimiter {
  constructor(kv) {
    this.kv = kv;
  }

  async checkLimit(identifier, maxRequests, windowSeconds) {
    const key = `rate_limit:${identifier}`;
    const now = Date.now();
    const windowMs = windowSeconds * 1000;

    // Get current count
    const data = await this.kv.get(key, { type: 'json' });

    if (!data || now - data.timestamp > windowMs) {
      // New window
      await this.kv.put(key, JSON.stringify({
        count: 1,
        timestamp: now
      }), { expirationTtl: windowSeconds });
      return true;
    }

    if (data.count >= maxRequests) {
      return false; // Rate limit exceeded
    }

    // Increment count
    data.count++;
    await this.kv.put(key, JSON.stringify(data), {
      expirationTtl: windowSeconds
    });

    return true;
  }
}

export default {
  async fetch(request, env, ctx) {
    const corsHeaders = handleCORS(request);
    if (request.method === 'OPTIONS') return corsHeaders;

    // Rate limiting
    const rateLimiter = new RateLimiter(env.RATE_LIMIT_KV);
    const clientIP = request.headers.get('CF-Connecting-IP');
    const allowed = await rateLimiter.checkLimit(clientIP, 100, 60);

    if (!allowed) {
      return new Response('Rate limit exceeded', {
        status: 429,
        headers: corsHeaders
      });
    }

    // Authentication
    const user = await authenticate(request, env);
    if (user instanceof Response) {
      return user; // Error response
    }

    // Process request
    const response = await handleRequest(request, env, user);

    // Add CORS headers to response
    Object.entries(corsHeaders).forEach(([key, value]) => {
      response.headers.set(key, value);
    });

    return response;
  }
};
```

## Caching Strategies

```javascript
// Cache API
async function cacheFirst(request) {
  const cache = caches.default;
  let response = await cache.match(request);

  if (!response) {
    response = await fetch(request);
    // Cache for 1 hour
    response = new Response(response.body, response);
    response.headers.set('Cache-Control', 'max-age=3600');
    await cache.put(request, response.clone());
  }

  return response;
}

// Stale-while-revalidate
async function staleWhileRevalidate(request, ctx) {
  const cache = caches.default;
  let response = await cache.match(request);

  // Background refresh
  ctx.waitUntil(
    fetch(request).then(freshResponse => {
      const clonedResponse = freshResponse.clone();
      cache.put(request, clonedResponse);
    })
  );

  if (response) {
    return response;
  }

  return fetch(request);
}

// Custom cache keys
function customCacheKey(request) {
  const url = new URL(request.url);

  // Ignore query parameters for caching
  url.search = '';

  // Add custom cache key based on headers
  const userAgent = request.headers.get('User-Agent');
  const isMobile = /mobile/i.test(userAgent);
  url.searchParams.set('device', isMobile ? 'mobile' : 'desktop');

  return new Request(url.toString(), request);
}

export default {
  async fetch(request, env, ctx) {
    const cacheKey = customCacheKey(request);

    if (request.url.includes('/api/')) {
      // API routes: stale-while-revalidate
      return staleWhileRevalidate(cacheKey, ctx);
    }

    // Static assets: cache first
    return cacheFirst(cacheKey);
  }
};
```

## Edge Functions

```javascript
// HTML rewriting
export default {
  async fetch(request, env, ctx) {
    const response = await fetch(request);

    // Inject analytics script
    return new HTMLRewriter()
      .on('head', new HeadInjector())
      .transform(response);
  }
};

class HeadInjector {
  element(element) {
    element.append(
      '<script>console.log("Injected at edge!");</script>',
      { html: true }
    );
  }
}

// Geolocation-based routing
export default {
  async fetch(request, env, ctx) {
    const country = request.cf.country;

    // Route based on country
    const apiEndpoint = {
      'US': 'https://us-api.example.com',
      'EU': 'https://eu-api.example.com',
      'default': 'https://global-api.example.com'
    }[country] || 'https://global-api.example.com';

    const url = new URL(request.url);
    const apiUrl = new URL(url.pathname, apiEndpoint);

    return fetch(apiUrl, request);
  }
};

// A/B testing
export default {
  async fetch(request, env, ctx) {
    const url = new URL(request.url);

    // Determine variant
    let variant = request.headers.get('Cookie')?.match(/variant=(\w+)/)?.[1];

    if (!variant) {
      variant = Math.random() < 0.5 ? 'A' : 'B';
    }

    // Fetch appropriate version
    const response = await fetch(`${url.origin}/variant-${variant}${url.pathname}`);

    // Set cookie
    const newResponse = new Response(response.body, response);
    newResponse.headers.set('Set-Cookie', `variant=${variant}; Path=/; Max-Age=86400`);

    return newResponse;
  }
};
```

## Wrangler CLI

```bash
# Initialize project
wrangler init my-worker

# Development server
wrangler dev

# Deploy to production
wrangler publish

# Deploy to specific environment
wrangler publish --env production

# Tail logs
wrangler tail

# KV operations
wrangler kv:namespace create "MY_KV"
wrangler kv:key put --namespace-id=<id> "key" "value"
wrangler kv:key get --namespace-id=<id> "key"

# Durable Objects
wrangler publish --new-class Counter

# Secrets
wrangler secret put SECRET_NAME
```

## wrangler.toml Configuration

```toml
name = "my-worker"
main = "src/index.js"
compatibility_date = "2024-01-01"

# KV bindings
kv_namespaces = [
  { binding = "MY_KV", id = "xxxxxxxx" },
  { binding = "CACHE_KV", id = "yyyyyyyy" }
]

# Durable Objects
[durable_objects]
bindings = [
  { name = "COUNTER", class_name = "Counter" },
  { name = "CHAT_ROOM", class_name = "ChatRoom" }
]

[[migrations]]
tag = "v1"
new_classes = ["Counter", "ChatRoom"]

# Environment variables
[vars]
ENVIRONMENT = "production"

# Routes
routes = [
  { pattern = "example.com/*", zone_name = "example.com" }
]

# Cron triggers
[triggers]
crons = ["0 */6 * * *"]
```

## Best Practices

### Performance
- Cache aggressively at the edge
- Minimize worker execution time
- Use Workers KV for global data
- Implement connection pooling
- Optimize cache keys
- Use stale-while-revalidate
- Minimize external requests

### Security
- Validate all inputs
- Implement rate limiting
- Use secrets for sensitive data
- Set security headers
- Implement CORS properly
- Use HTTPS everywhere
- Log security events

### Development
- Use TypeScript for type safety
- Test locally with wrangler dev
- Implement proper error handling
- Monitor worker metrics
- Use environment variables
- Version your workers
- Document API endpoints

## Anti-Patterns

❌ Storing large data in KV frequently
❌ Long-running computations in workers
❌ Not implementing caching
❌ Hardcoding secrets
❌ Ignoring rate limits
❌ No error handling
❌ Excessive external API calls

## Resources

- Cloudflare Workers: https://workers.cloudflare.com/
- Wrangler CLI: https://developers.cloudflare.com/workers/wrangler/
- Workers Examples: https://developers.cloudflare.com/workers/examples/
- Durable Objects: https://developers.cloudflare.com/workers/learning/using-durable-objects/
- Workers KV: https://developers.cloudflare.com/workers/runtime-apis/kv/

Overview

This skill provides expert-level guidance for Cloudflare Workers, CDN caching, edge computing, and Cloudflare security services. It focuses on practical patterns, code examples, and deployment workflows to build low-latency, secure edge applications.

How this skill works

It inspects common edge problems and offers concrete solutions: serverless routing, caching strategies, durable state, WebSocket handling, and security middleware. The guidance explains how to use Workers, Workers KV, Durable Objects, Cache API, and Wrangler CLI together. It also outlines production-ready patterns for authentication, rate limiting, CORS, and HTML rewriting at the edge.

When to use it

  • You need to reduce latency by running logic at the edge.
  • You want global, low-cost caching strategies for APIs or static assets.
  • You require distributed state or real-time features using Durable Objects.
  • You must implement edge security: WAF, rate limiting, or DDoS mitigation.
  • You are deploying serverless TypeScript Workers and need CI/dev tooling with Wrangler.

Best practices

  • Cache aggressively at the edge and prefer stale-while-revalidate for APIs.
  • Keep worker execution short; avoid long-running computations in Workers.
  • Store small, frequently-read global data in Workers KV; use Durable Objects for coordinated state.
  • Protect endpoints with rate limiting, input validation, and secrets stored via Wrangler.
  • Use TypeScript, local testing (wrangler dev), and monitor metrics and logs before production rollouts.

Example use cases

  • Global API gateway with stale-while-revalidate caching and KV-backed fallback.
  • Real-time chat or counters implemented with Durable Objects and WebSocket pairing.
  • Edge HTML rewriting to inject privacy-safe analytics or A/B testing variants.
  • Geolocation-based routing to regional API endpoints for compliance and latency optimization.
  • Rate-limited API endpoints with JWT authentication and CORS handling at the edge.

FAQ

When should I use Workers KV vs Durable Objects?

Use Workers KV for simple, highly-available key-value data and caching. Use Durable Objects when you need strongly-consistent, coordinated, or session-based state and in-memory session management.

How do I implement safe rate limiting at the edge?

Use a small KV namespace to track windows and counts or use Durable Objects for per-identifier counters. Combine limits with CF-Connecting-IP, short TTLs, and proper error responses (429) to protect backends.