Edge Computing with Cloudflare Workers: Moving Logic to Where Your Users Are
A developer's guide to Cloudflare Workers and edge computing — when to move logic to the edge, how to structure Worker code, and the trade-offs that most tutorials skip.

James Ross Jr.
Strategic Systems Architect & Enterprise Software Developer
Edge Computing with Cloudflare Workers: Moving Logic to Where Your Users Are
Most developers hear "edge computing" and think of CDNs caching static files. That is a piece of the picture, but it misses the more interesting part. Edge computing is about running your actual application logic — authentication checks, request routing, data transformations — on servers physically close to the user making the request. Not in a single us-east-1 region. Not behind a load balancer pointing to three availability zones in Virginia. On a machine in the same city as the person clicking the button.
I have been running production workloads on Cloudflare Workers for over a year now. Some of those decisions were obvious wins. Others taught me where the edge falls apart. This is the honest breakdown.
What Edge Computing Actually Is
A traditional server architecture looks like this: a user in Tokyo makes a request, that request crosses the Pacific Ocean, hits your load balancer in Oregon, gets routed to an application server, queries a database, and sends the response back across the Pacific. Round trip latency of 150 to 300 milliseconds before your code even runs.
Edge computing flips this. Your code runs on a network of globally distributed nodes — Cloudflare has over 300 points of presence worldwide. When the user in Tokyo makes that request, it hits a Cloudflare node in Tokyo. Your code executes there. If it can resolve the request without calling back to an origin server, the response is back to the user in single-digit milliseconds.
The key distinction from a CDN is that you are running arbitrary code, not just serving cached files. A CDN answers "here is a copy of that HTML file." An edge function answers "let me check your auth token, look up your geolocation, decide which variant of the page you should see, and return a response." That is a fundamentally different capability.
When Edge Makes Sense
Not every piece of logic belongs at the edge. The sweet spot is operations that are latency-sensitive, relatively lightweight, and do not require complex database transactions. Here is where I consistently reach for Workers.
Authentication and Authorization
Validating a JWT or session token is a perfect edge operation. The token is in the request headers. Validation is a cryptographic operation that does not require a database call. If the token is invalid, you reject the request at the edge before it ever touches your origin — saving compute and reducing attack surface.
Geolocation Routing
Cloudflare attaches geolocation data to every request automatically. Redirecting users to region-specific content, enforcing geographic compliance rules, or serving localized pricing — all of this runs naturally at the edge without external API calls.
A/B Testing and Feature Flags
Instead of loading a feature flag SDK on the client that makes its own network requests, resolve the flag at the edge. Read the experiment assignment from KV storage, modify the response, and the user never knows the decision happened. No layout shift, no flicker, no additional round trip.
Request Rewriting and Middleware
URL rewrites, header injection, CORS handling, rate limiting — these are operations that happen on every request and benefit enormously from running close to the user. I wrote about how this fits into the broader deployment picture in my Cloudflare Pages guide, where Workers handle the server-side logic alongside static frontends.
Cloudflare Workers Basics
Workers run in V8 isolates — the same JavaScript engine that powers Chrome, but without a full browser or Node.js environment. Startup time is under a millisecond because there is no cold container to provision. You write standard TypeScript, deploy with Wrangler, and Cloudflare handles the distribution.
The ecosystem has matured significantly. You can use Hono as a lightweight framework on top of Workers, connect to KV for global key-value storage, D1 for SQLite-based relational data, and R2 for S3-compatible object storage. The entire stack runs on Cloudflare's network without reaching out to AWS or GCP.
Here is a minimal Worker with Hono that handles routing:
import { Hono } from "hono";
import { cors } from "hono/cors";
import { jwt } from "hono/jwt";
type Bindings = {
CACHE: KVNamespace;
DB: D1Database;
JWT_SECRET: string;
};
const app = new Hono<{ Bindings: Bindings }>();
app.use("/api/*", cors());
app.use("/api/protected/*", jwt({ secret: (c) => c.env.JWT_SECRET }));
app.get("/api/products", async (c) => {
const cached = await c.env.CACHE.get("products:all", "json");
if (cached) {
return c.json(cached);
}
const { results } = await c.env.DB.prepare(
"SELECT id, name, price FROM products WHERE active = 1"
).all();
await c.env.CACHE.put("products:all", JSON.stringify(results), {
expirationTtl: 300,
});
return c.json(results);
});
export default app;
This gives you routing, CORS, JWT authentication, KV caching, and D1 database access in under 30 lines. The entire thing deploys to 300-plus locations in about 15 seconds.
Code Examples: Solving Real Problems at the Edge
Geolocation-Based Routing
Cloudflare provides cf properties on every request with detailed geolocation data. No third-party API needed.
export default {
async fetch(request: Request): Promise<Response> {
const country = request.cf?.country as string;
const region = request.cf?.region as string;
// Enforce GDPR compliance — EU users hit EU-hosted API
const euCountries = new Set([
"DE", "FR", "IT", "ES", "NL", "BE", "AT", "PL",
"SE", "DK", "FI", "IE", "PT", "GR", "CZ", "RO",
]);
const apiBase = euCountries.has(country)
? "https://api-eu.example.com"
: "https://api-us.example.com";
const url = new URL(request.url);
const apiUrl = `${apiBase}${url.pathname}${url.search}`;
return fetch(apiUrl, {
method: request.method,
headers: request.headers,
body: request.body,
});
},
};
Edge Middleware: Rate Limiting with KV
type Env = {
RATE_LIMIT: KVNamespace;
};
const WINDOW_MS = 60_000;
const MAX_REQUESTS = 100;
export default {
async fetch(request: Request, env: Env): Promise<Response> {
const ip = request.headers.get("cf-connecting-ip") ?? "unknown";
const key = `rate:${ip}`;
const current = await env.RATE_LIMIT.get(key, "json") as {
count: number;
resetAt: number;
} | null;
const now = Date.now();
if (current && now < current.resetAt) {
if (current.count >= MAX_REQUESTS) {
return new Response("Too Many Requests", {
status: 429,
headers: {
"Retry-After": String(Math.ceil((current.resetAt - now) / 1000)),
},
});
}
await env.RATE_LIMIT.put(
key,
JSON.stringify({ count: current.count + 1, resetAt: current.resetAt }),
{ expirationTtl: 120 }
);
} else {
await env.RATE_LIMIT.put(
key,
JSON.stringify({ count: 1, resetAt: now + WINDOW_MS }),
{ expirationTtl: 120 }
);
}
// Pass through to origin
return fetch(request);
},
};
This is approximate rate limiting — KV is eventually consistent, so a burst of simultaneous requests might slip past the limit briefly. For most applications, that is acceptable. For financial-grade rate limiting, you would want Durable Objects instead.
Workers vs Traditional Serverless
I have run workloads on Lambda, Cloud Functions, and Workers. Here is my honest comparison.
Cold starts. Lambda cold starts range from 100ms to several seconds depending on runtime and bundle size. Workers cold starts are effectively zero — V8 isolates spin up in under a millisecond. For user-facing endpoints, this difference is night and day.
Global distribution. Lambda runs in whichever region you deploy to. Multi-region Lambda requires explicit configuration, multiple deployments, and usually DynamoDB global tables. Workers deploy globally by default. There is no configuration step for "make this available worldwide."
Runtime environment. Lambda gives you a full Node.js (or Python, Go, etc.) runtime. Workers give you a stripped-down V8 isolate. If your code depends on Node.js APIs, native modules, or heavy server-side libraries, Workers will fight you. Lambda will not.
Cost. Workers' free tier covers 100,000 requests per day. Paid plans start at $5/month for 10 million requests. Lambda pricing is more complex — you pay for invocations, duration, and memory — but for equivalent workloads, Workers tend to be cheaper. I covered broader cost strategies in my cloud cost optimization guide.
Execution limits. Lambda allows up to 15 minutes of execution time and 10GB of memory. Workers cap at 30 seconds on the paid plan (10ms CPU on free) and 128MB of memory. These are fundamentally different constraint profiles.
The Limitations Nobody Talks About
No Long-Running Processes
If your operation takes more than 30 seconds, Workers is the wrong tool. Background jobs, video processing, complex data pipelines — these belong on traditional compute. Workers are designed for request-response cycles, not batch processing.
128MB Memory Ceiling
You cannot load a large ML model, process a massive CSV, or hold a significant dataset in memory. Workers are for lightweight, focused operations. If you are doing heavy computation, you need a server.
Eventually Consistent Storage
KV reads are fast globally. KV writes take up to 60 seconds to propagate. If your application requires strong consistency — "write then immediately read back the same value from another region" — KV alone will not satisfy that requirement. D1 provides stronger consistency but is a single-region database under the hood, which partially defeats the purpose of edge distribution.
The V8 Isolate Environment
Workers do not run Node.js. They run in V8 isolates with Web API compatibility. Many npm packages work fine. Some do not — anything touching fs, child_process, Node-specific crypto, or native C++ bindings will fail. You will spend time finding Workers-compatible alternatives or restructuring code. Libraries have gotten much better about this, but it remains a real friction point.
When NOT to Put Things at the Edge
This is the part most Cloudflare tutorials skip. Edge computing is not universally better. Here are cases where a centralized server is the right call.
Database-heavy operations. If every request requires multiple database queries, and your database lives in a single region, running your code at the edge just adds a network hop back to the database. You have moved your compute farther from your data. The user sees higher latency, not lower. This is the most common mistake I see. The techniques I covered in API performance optimization matter more than where your code runs if your bottleneck is database round trips.
Complex business logic. Multi-step transactions, workflow orchestration, operations involving multiple services — these benefit from co-location with your data layer and other services, not from geographic distribution.
Large dependencies. If your application needs heavy libraries — image processing, PDF generation, ML inference — the 128MB memory limit and the absence of native modules make Workers impractical. Use a proper server.
Development velocity. Workers have a different debugging model, different runtime constraints, and a smaller ecosystem. If your team is not familiar with the platform, the learning curve costs development speed. Sometimes the 200ms round trip to a traditional server is a perfectly acceptable trade-off for faster iteration.
The Decision Framework
My mental model is straightforward. If the operation is stateless or reads from eventually consistent storage, runs in under 50ms of CPU time, and serves users globally — put it at the edge. If it requires strong consistency, complex transactions, or heavy compute — keep it centralized.
The edge is not a replacement for your server. It is a layer in front of it that handles the work that benefits from geographic proximity. Get that boundary right and you get the performance gains without fighting the platform.