Edge functions
Status: 🟩 COMPLETE Last updated: 2026-06-19 Plain-English tagline: A lightweight kind of serverless function that runs on hundreds of small servers spread across the world, near every user — fast cold starts, tiny memory limits, runs JavaScript without full Node.
In plain English
A regular serverless function lives in ONE region. To run code, the platform spins up a container with Node.js (or Python, Go, etc.) inside it, runs your function, and shuts down. That’s fast in the same region, slow if the user is on the other side of the world.
Edge functions are a different shape of serverless that fixes that. They run on a CDN-like global fleet — hundreds of cities — and at each location they run as V8 isolates (the same engine Chrome uses) instead of full containers.
The trade-off:
| Property | Serverless function | Edge function |
|---|---|---|
| Where it runs | One region | Every edge city globally |
| Runtime | Full Node.js (or chosen) | V8 isolate (JS/TS/WASM only, Web Standard APIs) |
| Cold start | 200–2000ms | < 5ms |
| Memory limit | Up to 10GB | ~128MB |
| CPU time | Up to minutes | 10ms free / 30s paid (Workers); similar limits elsewhere |
| Node-specific modules | Yes | No |
| Filesystem | /tmp available | Not available |
| Best for | Heavy work, database-bound | Fast checks, redirects, header rewrites, lightweight APIs |
Edge functions exchange power for proximity and speed. They’re the right tool when “happens close to the user, almost instantly” matters more than “can do anything.”
In 2026, edge runtimes are the foundation of:
- Cloudflare Workers — the canonical edge runtime
- Vercel Edge Functions — Vercel’s edge layer
- Netlify Edge Functions — Deno-based variant
- Deno Deploy — Deno’s own edge platform
- Fastly Compute@Edge — WASM-based edge
Despite different brand names, they’re conceptually the same thing: small JavaScript runtimes near the user.
Why it matters
Three reasons edge functions are important:
-
Latency wins compound. A user in Sydney hitting an edge function gets a 10ms response. The same user hitting a US-region function pays ~300ms of network. If your function makes 2-3 such round trips (auth check, redirect, A/B assignment), edge saves 600-900ms — a full second of user-perceived speed.
-
Cold starts disappear. A traffic spike at 3am triggers cold starts on classic serverless; users hit them slowly. V8 isolates spin up in < 5ms. Effectively no cold-start problem.
-
Some platforms make edge the default. Cloudflare Workers IS edge — there’s no “regional” option. Building on Cloudflare means working in the edge model from day one.
The catch: not every workload fits. A handler that needs Puppeteer, FFmpeg, native database drivers, or 2GB of RAM will not work at the edge.
What “the edge” actually consists of
Each major edge platform runs its own fleet:
| Platform | Edge cities | Notes |
|---|---|---|
| Cloudflare | 320+ cities | Largest network; “every internet user within ~50ms” |
| Vercel Edge Network | Backed by Cloudflare infrastructure for some routing + own POPs | Tightly integrated with Next.js |
| Netlify Edge | Backed by Deno Deploy infrastructure (~35+ regions) | Deno runtime |
| Fastly | 90+ POPs, designed for media/ecommerce | WASM runtime |
| Deno Deploy | 35+ regions | Pure Deno runtime |
| AWS CloudFront Functions | 400+ AWS POPs | Extremely limited runtime; for header rewrites only |
| AWS Lambda@Edge | Regional edge — runs in US/EU/Asia regional caches, not at every POP | Less true-edge than the others |
The user’s request hits the nearest POP via DNS-level geographic routing. The same code runs at every POP, isolated per request.
What V8 isolates actually do
A V8 isolate is a tiny sandbox running the same JS engine that powers Chrome:
- Spin-up time: microseconds to milliseconds
- Memory footprint: small (~10MB baseline)
- Isolated from other isolates: an isolate can’t reach another isolate’s memory
- Can run JS, TypeScript (transpiled), WASM
Compare to a container (Lambda-style):
- Container starts in 200-2000ms
- Each one consumes its own dedicated memory (128MB minimum)
- Network and filesystem virtualization adds overhead
- Boots a whole language runtime + your code
This is why edge runtimes can pack thousands of isolates per server and still respond fast. Lambda has to boot a fresh container per cold concurrency unit.
Implication: edge runtimes can’t expose Node.js native modules. There’s no Node here — just V8 with Web Standard APIs grafted onto it.
What APIs ARE available at the edge
The edge runtimes converge on Web Standards (the same APIs that work in browsers):
fetch,Request,Response,Headers,URLcrypto.subtlefor cryptographyTextEncoder,TextDecoderReadableStream,WritableStreamsetTimeout,Promise,async/awaitconsole.log- Platform-specific bindings (Cloudflare’s KV/D1/R2 via globals; Vercel’s
geo/iphelpers via headers)
What’s NOT available (or only partially):
fs(no filesystem)child_process(no shell)net,dgram(no raw sockets)crypto.createCipherivetc. (the old Node crypto API — usecrypto.subtleinstead)- Many npm packages that depend on the above
A package can be “edge-compatible” by avoiding Node-only APIs. Modern libraries increasingly publish edge-compatible variants — Anthropic SDK, OpenAI SDK, Stripe SDK, Supabase client all work at the edge in 2026.
A concrete example: an edge function
Vercel Edge Function declared in Next.js:
// app/api/geo/route.ts
import { NextRequest, NextResponse } from "next/server";
export const runtime = "edge";
export async function GET(req: NextRequest) {
const country = req.geo?.country ?? "unknown";
const city = req.geo?.city ?? "unknown";
return NextResponse.json({
message: `Hello from ${city}, ${country}`,
edgeRegion: process.env.VERCEL_REGION,
});
}A user in Sydney gets { message: "Hello from Sydney, AU", edgeRegion: "syd1" } in ~15ms. A user in Berlin gets { message: "Hello from Berlin, DE", edgeRegion: "fra1" } in ~15ms. Same code, different region runs it, fast everywhere.
The runtime = "edge" export tells Next.js / Vercel to deploy this route as an Edge Function rather than a Node serverless function.
A Cloudflare Worker version of the same is similar:
export default {
async fetch(req: Request): Promise<Response> {
const country = req.cf?.country ?? "unknown";
return new Response(JSON.stringify({ message: `Hello from ${country}` }), {
headers: { "content-type": "application/json" },
});
},
};Same shape, different platform glue.
What edge functions are great at
The list of canonical use cases:
-
Auth checks before reaching origin. “Is this user logged in? If not, redirect to login.” A handful of bytes — auth check, redirect — runs at the edge in 10ms. The origin server is only consulted when needed.
-
Geographic redirects. Send AU users to
au.example.com, EU users toeu.example.com. Decision made at the edge based onreq.geo. -
A/B testing assignment. Read a cookie or hash the user ID; pick a variant; rewrite the request path. No origin round-trip needed.
-
Header rewrites. Add security headers, strip cookies for caching, normalize URLs. All before reaching origin.
-
Bot blocking and rate limiting. Identify bad actors at the edge, return 429 without involving origin.
-
Image manipulation on the fly. Resize, reformat (AVIF/WebP), watermark. Cloudflare Images, Vercel Image, Bunny CDN all use edge for this.
-
ISR revalidation. Cached HTML pages served from the edge; revalidation logic at the edge decides when to ask origin for fresh data.
-
Lightweight APIs that don’t touch a database. Currency conversion, timezone lookups, response transformation.
-
Edge middleware. In Next.js, the
middleware.tsfile at the project root runs at the edge for EVERY request before it reaches origin. Perfect for auth gates, request-aware redirects.
Edge middleware specifically — the Next.js superpower
In any Next.js project, you can drop a middleware.ts at the project root:
// middleware.ts
import { NextResponse, NextRequest } from "next/server";
export function middleware(req: NextRequest) {
// Auth gate — check session cookie
const session = req.cookies.get("session");
if (!session && req.nextUrl.pathname.startsWith("/dashboard")) {
return NextResponse.redirect(new URL("/login", req.url));
}
// Add a header
const res = NextResponse.next();
res.headers.set("x-deployment", "prod");
return res;
}
export const config = {
matcher: ["/dashboard/:path*", "/admin/:path*"],
};This runs at the edge for every matching path. It can:
- Redirect, rewrite, or block requests
- Modify request and response headers
- Read cookies and geo info
- Set new cookies
- Decide whether the request even reaches origin
It cannot:
- Open database connections (no Node-native drivers)
- Run heavy computation (10ms budget per request on Vercel’s edge)
- Use Node-only npm packages
Edge middleware is THE place to implement “things that should happen on every request before the page renders.”
What edge functions are NOT good at
The list of bad fits:
-
Heavy database access from edge. The DB is in a region; if your edge function in Tokyo makes 5 queries to a Sydney DB, you’ve added 5 × ~120ms to the request — undoing the edge advantage. Cache at the edge, or use Hyperdrive / similar pooler.
-
Heavy CPU. 10ms CPU budget on Cloudflare free tier; 30s paid. Heavy AI inference, image transcoding, large-document parsing all blow past these.
-
Streaming long responses. Some edge runtimes support streaming, but with constraints. For minute-long LLM streams, regional serverless or dedicated infra often works better.
-
Large dependencies. Edge function bundle size limits range from 1MB to a few MB. A 50MB Node deps tree won’t fit.
-
Node-only libraries. Anything that reaches for
fs,child_process, native bindings (sharp, canvas), or old Node crypto. Many libraries publish edge-compatible variants; many don’t. -
Stateful sessions in memory. Each edge node has its own memory, but requests from one user can hit different nodes. Never assume in-memory state persists.
Hybrid patterns — edge + regional working together
In most production webapps, edge and regional functions work together:
- Edge middleware at the front: auth check, geo redirect, A/B assignment, cache header normalization.
- Regional serverless functions for routes that need the database: API endpoints, server-rendered pages, server actions.
- CDN caching at the edge in front of both: static assets, ISR’d pages, cacheable API responses.
This is what Vercel’s “Edge Network + Functions” automatically composes for you. The mental model: edge for routing decisions and protection; regional for the actual work; CDN for caching.
A note on Cloudflare Workers specifically
Cloudflare Workers were the original “real edge runtime” — V8 isolates, ~5ms cold starts, global by default. They predate (and inspired) most other edge platforms.
Workers have evolved into a near-complete backend stack:
- Workers — the function runtime
- Bindings — declarative access to D1 (SQL), R2 (storage), KV (key-value), Queues, Durable Objects
- No connection strings — bindings ARE the connection
- Pricing — 10M requests/month free; 30s CPU per request paid
For projects designed Cloudflare-first, Workers can replace BOTH edge middleware and regional functions. It’s a different tradeoff space but increasingly viable.
For Next.js apps deployed to Vercel, you use Vercel Edge Functions instead — same isolate runtime, different platform integration.
See Cloudflare for the broader picture.
Common gotchas
-
“Edge” means “no Node APIs.” A library you used in a regular API route may break when you flip
runtime = "edge". The error is usually a runtime-time “module not found” or “X is not defined.” -
Bundle size limits are tight. 1MB on Cloudflare Workers (free), 4MB on Vercel Edge (after gzip), etc. Importing a heavy library — even tree-shaken — can blow the budget. Audit with platform CLIs.
-
fetchis the only outgoing call mechanism. Nopgdriver, nomysql2— only HTTP. Use the database’s HTTP API (Supabase REST, PlanetScale’s HTTP driver, Neon’s serverless driver) or a pooler. -
No timezone or system locale. Different edge nodes may have different default locales. Always set timezone explicitly (e.g. ISO strings, explicit
Intl.DateTimeFormatoptions). -
The user’s IP isn’t
req.socket.remoteAddress. Edge proxies addcf-connecting-ip,x-forwarded-for, etc. Read the platform docs for the right header. -
Each edge node has its own memory. A counter in module scope doesn’t sum across nodes. Use KV / D1 / Redis for shared state.
-
GeoIP is approximate. A user in a VPN appears in the VPN exit’s country. Don’t make critical decisions on geo alone — confirm with explicit user input where it matters.
-
Edge functions can be invoked from anywhere, including from your own regional functions. Don’t assume an edge function call is “from a browser.” Validate auth on every call.
-
WebSocket support is platform-specific. Cloudflare Workers + Durable Objects can hold websocket connections. Vercel Edge cannot. Read the docs.
-
CPU time vs wall-clock time matter differently. On Cloudflare free tier, you have 10ms of CPU but unlimited wall-clock (waiting for
fetch). Heavy CPU work is the constraint, not external API calls. Plan accordingly. -
crypto.subtleis async; oldcrypto.createHashis not. Refactoring from Node to edge often means rewriting crypto code from sync to async. -
Cookies have edge-specific quirks. Some edge runtimes parse cookies for you (
req.cookies.get(...)); some give you the raw header. Some support setting cookies via response headers; some via a dedicated API. -
Streaming responses work but vary.
ReadableStreamis supported, but back-pressure handling differs. Server-Sent Events (SSE) work cleanly; raw HTTP/2 streaming is more constrained. -
Cold start within the V8 isolate model can still bite the first deploy. A brand-new function on a never-warmed isolate may take ~10ms more on first hit. Negligible most of the time but real.
-
Logging is sampled or rate-limited. Heavy
console.logfrom edge functions can be dropped by the platform. Use structured logging and route critical events to a real log sink (Vercel Analytics, Logflare, etc.). -
Errors at the edge can be hard to attribute. Which node ran the request? Which version? Platforms emit headers like
cf-ray,x-vercel-idto help correlate logs. Capture them. -
process.env.Xmay not work the same way. Cloudflare exposes env via bindings, notprocess.env. Vercel emulatesprocess.envfor compatibility. Don’t assume. -
Calling your own regional function from an edge function is two hops. The user → edge → regional → DB path has more network latency than user → regional → DB. Use edge for things that don’t need to call regional; otherwise just use regional.
-
Edge runtime mode in Next.js used to be opt-in per route. Now mixing edge and Node in the same project is normal. But you can’t import shared utilities that use Node APIs from edge routes. The
server-onlyandclient-onlypackages help draw the line. -
Pricing models differ wildly. Cloudflare Workers is per-request + per-CPU-ms. Vercel Edge is per-request + per-duration. Compare the math for your workload pattern.
-
Don’t confuse edge functions with edge cache. The CDN caches static responses; edge functions run code. Both happen “at the edge” but they’re different products with different mental models.
-
The 5-second SSE warning. Some platforms close idle SSE connections at 5–30 seconds. For long-running LLM streams, keep-alive pings every few seconds prevent disconnection.
See also
- Serverless functions 🟩 — the regional cousin
- Server actions (Next.js) 🟩 — can be edge-runtime’d
- What is a backend? đźź©
- Cloudflare 🟩 🟦 — the canonical edge platform
- Vercel 🟩 🟦 — Vercel Edge Functions
- Regions & edge 🟩 — the foundational concept
- CDNs 🟩 — the cache layer edge functions often sit alongside
- Node.js 🟩 🟦 — what edge runtimes are NOT
- APIs — the big picture 🟩
- Next.js 🟩 🟦 —
middleware.tsruns at the edge - Glossary: Edge, V8, Isolate
Sources
- Cloudflare Workers docs — the canonical edge platform
- Vercel Edge Functions
- Vercel middleware
- Netlify Edge Functions
- Deno Deploy
- Cloudflare — How Workers work — the V8 isolates manifesto
- WinterCG — the cross-edge standards body