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
// 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
// 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
// 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
// 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
// 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
// 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
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
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/