Vercel Edge Middleware: When to Use It and What Breaks
Edge Middleware moves auth, routing, and feature flags to the edge — but only if you respect the 1.5 MB bundle, 50 ms CPU, and no-request-body limits. Here's how to decide when it fits and how to verify it works.
17 Feb 2026, 16:05 UTC

The problem: request logic at the edge vs. serverless
You need to check authentication, route by geography, or evaluate a feature flag before a request reaches your Next.js page or API route. Vercel Edge Middleware runs on V8 isolates at the edge — no cold starts, no Node.js runtime — but it comes with hard limits: 1.5 MB gzipped bundle, 50 ms CPU time, and no access to request bodies for POST/PUT. Choosing Middleware over a serverless function is a concrete engineering trade-off, not a default.
Runtime constraints that shape the decision
Edge Middleware executes in a V8 isolate, not Node.js. That means no fs, no native addons, a subset of crypto, and a 1.5 MB bundle ceiling after compression. Importing a full ORM (Prisma client ~2 MB) or a heavy PDF parser will fail at deploy time with Function body size limit exceeded. The 50 ms CPU budget is strict: a tight loop of 100 million iterations triggers EXCEEDED_CPU_TIME and returns a 502. Local vercel dev runs a Node.js sandbox, so missing globals (e.g., TextEncoder in older isolates) only surface on preview or production.
What Middleware actually handles well
- Auth guards: read cookies, validate JWTs with
jose(small, edge-compatible), redirect or rewrite to login. - Geo routing:
request.headers.get('x-vercel-ip-country')lets you redirect/pricingto/pricing/uswithout hitting the origin. - Feature flags & A/B: Edge Config (key-value store replicated globally) reads in sub-millisecond latency. No external API call, no cold start.
- Bot detection: inspect
user-agentandcf-botheaders; respond with 403 or rewrite to a challenge page.
All of these run before the route handler, so static assets and API routes outside the matcher stay fast.
Matcher configuration: the silent latency tax
The matcher in middleware.ts (or vercel.json) decides which paths invoke Middleware. A broad matcher: ['/:path*'] intercepts every request — including /_next/static/ chunks and /api/health — adding ~5–10 ms per request. Narrow it:
// middleware.ts
export const config = {
matcher: [
'/dashboard/:path*',
'/settings/:path*',
'/api/protected/:path*'
]
};
Verify in Function Logs: filtered paths show edge runtime entries; static asset requests should not appear.
Worked example: feature flag with Edge Config
Create a flag new-checkout in the Vercel Edge Config dashboard (project → Edge Config → Add item). In Middleware:
// middleware.ts
import { NextResponse } from 'next/server';
import { get } from '@vercel/edge-config';
export async function middleware(request) {
const flag = await get('new-checkout'); // boolean
if (flag && request.nextUrl.pathname === '/checkout') {
const url = request.nextUrl.clone();
url.pathname = '/checkout/v2';
return NextResponse.rewrite(url); // internal proxy, no extra round-trip
}
return NextResponse.next();
}
Deploy to preview. In Function Logs, filter by edge runtime; the invocation should show duration under 5 ms. The rewrite preserves the original URL in the browser while serving the new checkout page.
Caching and cookie propagation gotchas
Middleware responses are not cached by Vercel's Edge Network unless you return a direct Response with Cache-Control. Rewrites and redirects inherit the destination's cache behavior. For cookies: Set-Cookie on a rewrite response only applies if the rewritten destination doesn't override it. Reliable pattern:
const response = NextResponse.next();
response.headers.set('Set-Cookie', 'session=abc; Path=/; HttpOnly; Secure');
return response;
This ensures the cookie survives the internal proxy.
Limitations you cannot work around
- No request body: POST/PUT bodies are unreadable in Middleware. Forward to an API route (
NextResponse.next()) and parse there. - No shared memory: Middleware runs in a different region than your serverless functions. Use Edge Config, Vercel KV, or an external store for state.
- 50 ms CPU ceiling: heavy regex, large JSON parsing, or crypto (e.g., RSA verify) will 502. Offload to a serverless function if logic exceeds ~30 ms.
Verify before you ship
- Deploy a minimal
middleware.tsto a preview branch. Open Vercel Function Logs → filteredgeruntime. Confirm your matcher paths appear; static assets do not. - Add
console.log(JSON.stringify(request.headers)); redeploy. Check that headers match the documentedHeadersAPI subset (nohostmanipulation). - Import a ~2 MB package (e.g.,
pdf-parse) in Middleware; deployment must fail with bundle size error. - Trigger a CPU-heavy loop (
for(let i=0;i<1e8;i++){}) and confirm 502 withEXCEEDED_CPU_TIMEin logs.
If all four checks pass, your Middleware is within the runtime envelope. Monitor edge invocation counts and durations weekly; a sudden spike usually means a matcher widened or a flag evaluation grew.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.