Skip to main content
btheo.com btheo.com > press start to play
NEW POST: NODE.JS SECURITY 2025 OPEN FOR FREELANCE 10+ YEARS EXP REACT × NODE × AWS NEW POST: NODE.JS SECURITY 2025 OPEN FOR FREELANCE 10+ YEARS EXP REACT × NODE × AWS
CLOUDFLARE WORKERS 5 MIN READ

Cloudflare Workers vs Node.js: When Edge Wins

WARNING · DRAGON AHEAD

Your Node.js API adds 100ms of latency. Not because your code is slow—because physics.

Cloudflare Workers run on 300+ edge locations worldwide. Your request hits the closest one first. No VPC routing, no cold start, no regional bottleneck.

But Workers are not a Node.js replacement. They’re a scalpel. This is how to know when to use one.

What Edge Runtime Means (And Doesn’t)

Cloudflare Workers run on V8 isolates—tiny JavaScript contexts with no process overhead:

// Cloudflare Worker - runs in ~1ms globally
export default {
async fetch(request) {
return new Response("Hello from edge", {
headers: { "Cache-Control": "max-age=3600" }
});
}
};
MetricNode.js (us-east-1)Cloudflare Worker (global edge)
P50 latency (client in Tokyo)140ms18ms
P95 latency (client in Sydney)180ms22ms
Time to first byte~50ms~2ms
Cold startN/A0ms (always warm)

That 160ms win is physics you can’t buy your way out of on a traditional server.

What Workers Can’t Do (And This Matters)

⚠️ No filesystem. Workers can’t read/write disk. Use KV (key-value cache) or Durable Objects (stateful storage).

// This fails in Workers:
const data = fs.readFileSync("config.json");
// This works:
const data = await env.KV.get("config");

⚠️ 30-second timeout. A long-running job that takes 2 minutes? Doesn’t fit. Move to background jobs (Queues, or call your origin Node.js service).

⚠️ No raw TCP/UDP. WebSockets work. Raw socket connections don’t. If you’re building an IRC client, use Node.js.

⚠️ Limited CPU time. Workers get ~50ms of CPU before they timeout. Parsing 100MB of JSON? Use your origin. Parsing 10KB? Fine.

⚠️ No long-running connections. HTTP keep-alives timeout at 600 seconds. WebSocket can hold longer, but state isn’t persisted across requests.

Real Use Cases: Where Edge Wins

1. Auth Middleware (✔ Perfect for Workers)

// wrangler.toml routes all requests through this Worker first
[env.production]
routes = [
{ pattern = "example.com/*", zone_name = "example.com" }
]
// worker.js
export default {
async fetch(request, env) {
const token = request.headers.get("Authorization");
// Check if token is in cache (KV)
const cached = await env.KV.get(`token:${token}`);
if (cached === "valid") {
return fetch(request); // Forward to origin
}
// Cache miss? Validate at origin
const origin = await fetch(new Request("https://origin.internal/verify", {
method: "POST",
body: JSON.stringify({ token })
}));
const { valid } = await origin.json();
if (valid) {
await env.KV.put(`token:${token}`, "valid", { expirationTtl: 3600 });
return fetch(request); // Forward to origin
}
return new Response("Unauthorized", { status: 401 });
}
};

Result: Token validation at edge (18ms) vs origin (140ms). 7x faster, cached, global.

2. Image Transforms & CDN

// Resize images on-the-fly, cache globally
export default {
async fetch(request, env) {
const url = new URL(request.url);
const width = url.searchParams.get("width") || 300;
const cacheKey = `image:${url.pathname}:${width}`;
// Check if transformed version is cached
const cached = await env.KV.get(cacheKey, { type: "arrayBuffer" });
if (cached) {
return new Response(cached, {
headers: { "Content-Type": "image/webp", "Cache-Control": "max-age=86400" }
});
}
// Fetch original from origin
const image = await fetch(`https://origin.com${url.pathname}`);
// In real code, use a library like Cloudflare's Image Resizing
return image;
}
};

Result: First user waits for resize. Second user gets cached 18ms response from nearest edge.

3. A/B Routing

// Route 5% of traffic to canary, 95% to stable
export default {
async fetch(request, env) {
const variant = Math.random() < 0.05 ? "canary" : "stable";
const target = variant === "canary"
? "https://canary.internal.com"
: "https://stable.example.com";
const response = await fetch(target + new URL(request.url).pathname);
response.headers.set("X-Variant", variant);
return response;
}
};

Result: No Node.js logic needed. Edge handles routing. Your origin stays simple.

4. Geo-Personalization

// Serve different content based on country
export default {
async fetch(request, env) {
const country = request.headers.get("cf-ipcountry") || "US";
const currency = { US: "USD", GB: "GBP", JP: "JPY" }[country] || "USD";
const response = await fetch(request);
// Inject currency into response (or cache different versions)
return new Response(response.body, response);
}
};

Result: Same endpoint, different data globally. No geography-aware routing needed at origin.

The Wrangler Setup

wrangler.toml
name = "my-worker"
type = "javascript"
account_id = "your-account-id"
workers_dev = true
[env.production]
name = "my-worker-prod"
routes = [
{ pattern = "api.example.com/*", zone_name = "example.com" }
]
kv_namespaces = [
{ binding = "KV", id = "abc123..." }
]
[build]
command = "npm run build"
cwd = "./"

Deploy:

Terminal window
wrangler publish --env production

Hybrid: Workers + Origin Node.js

The real pattern is not Workers instead of Node.js. It’s Workers in front of Node.js:

User Request
Cloudflare Worker (edge, 18ms)
├─ Cache hit? → Return immediately
├─ Auth valid? → Forward to origin
└─ Stale/miss → Fetch from origin, cache, return
Your Node.js API (origin, 140ms)
└─ Database, logic, everything

Worker latency is hidden. Users see:

  • Cache hit: 18ms (edge only)
  • Cache miss: 140ms origin + 18ms edge caching = 158ms first, 18ms second

Decision Matrix

NeedWorkersNode.js
Global latency-sensitive requests⚠️ Regional lag
Cache + serve at edge⚠️ CDN only
Auth/rate-limiting layer⚠️ Origin overhead
Long-running jobs (>30s)⚠️ Use origin
Database queries⚠️ Call origin
Complex business logic⚠️ Call origin
Stateful connections⚠️ Durable Objects
Team familiarity⚠️ New runtime

Summary

Workers are not Node.js.js replacements. They’re force multipliers for latency-sensitive, cache-friendly, stateless logic.

Use Workers for: ✔ Auth, routing, rate-limiting ✔ CDN/cache layers ✔ Geo-personalization ✔ Request filtering

Use Node.js (or call it from Workers) for: ✔ Database mutations ✔ Complex algorithms ✔ Background jobs ✔ Stateful services

The hybrid approach wins: edge for latency, origin for logic. You get physics-defying speed without sacrificing the stability of your existing stack.

Don’t ask “Workers or Node.js?” Ask “Workers in front of Node.js?”

ALL POSTS →