Next.js Middleware in 2026: Auth Guards, A/B Tests, and What Belongs at the Edge
Table of Contents
About the Author

Ahmed Mahmoud
Author & Developer
Software engineer passionate about web development and user experience design.
Founder of Devya · eng-ahmed.com ↗Key takeaways
Next.js Middleware (middleware.ts at the project root) intercepts every matched request before cache lookup, rendering, and route execution — the right layer for auth redirects, locale detection, and A/B cookie bucketing.
Middleware can read requests, set cookies, redirect, rewrite, or return an early response without the route running. Database queries and large npm imports do not belong here — both add latency to every matched request.
In 2026 on Vercel, Middleware runs on Fluid Compute (standard Node.js), not the old stripped edge runtime. The real constraint is latency: each added millisecond is paid on every matched request.
The matcher config scopes Middleware to specific routes. Without it, Middleware runs on every request including static files, adding overhead for no reason.
Auth in Middleware means verifying a self-contained JWT without a DB round trip. Full session validation belongs in the route or Server Component.
What Is Next.js Middleware and Where Does It Fit in the Request Lifecycle?
Next.js Middleware is a function exported from middleware.ts at the project root. It intercepts every matched request before Next.js resolves the route, before cached responses are served, and before any Server Component or Route Handler runs. It receives a NextRequest and returns a NextResponse: pass through (NextResponse.next()), redirect, rewrite (serve a different URL while keeping the original in the browser address bar), or a direct response that short-circuits the route.
Without a matcher config, Middleware runs on everything — including static files and images. The standard negative lookahead that excludes _next/static and image optimization routes is the correct starting point. On Vercel in 2026, Middleware runs on Fluid Compute (standard Node.js), so old Edge Runtime API restrictions no longer apply. Aim for under 10 ms execution time.
How Do We Write an Auth Guard in Next.js Middleware?
An auth guard reads a JWT from a cookie, verifies it locally with the jose library without a database call, and redirects to the login page if the token is missing or invalid. The JWT must be self-contained and signed — an opaque session ID requires a DB round trip, which breaks the latency budget for Middleware.
Two implementation details: the redirect target must be a URL object (new URL('/login', request.url), not a bare string), and when the token is invalid, delete the cookie on the redirect response to prevent a redirect loop from a malformed token being re-sent on every request.
How Do We Run A/B Tests in Next.js Middleware?
A/B testing in Middleware follows three steps: check for an existing bucket-assignment cookie, assign a bucket if absent, and rewrite the request to the variant URL. The rewrite keeps the original URL in the browser address bar — share links always point to the control route. Analytics read the cookie to attribute conversions to the correct variant.
Each variant lives in a private directory prefixed with an underscore, which Next.js treats as private and does not expose as a navigable route. Middleware rewrites /pricing to /_experiments/pricing/control or /_experiments/pricing/variant-b based on the cookie, transparent to the user.
What Can Middleware Read, Write, and Return?
Middleware receives a NextRequest with typed helpers: request.cookies, request.nextUrl, request.ip, request.geo (Vercel-only), and request.headers. We forward resolved values downstream by injecting custom headers into NextResponse.next() — this is how we pass a resolved locale or user ID to Server Components without re-running detection logic in every layout.
The four return types are: pass through (NextResponse.next()), redirect (browser navigates and URL changes), rewrite (browser URL unchanged, different content served), and a direct response (route never runs). Use redirect for auth flows and rewrite for A/B tests and path aliases.
What Should We NOT Put in Next.js Middleware?
Database queries are the most common mistake. Every matched request pays the DB round-trip cost before the route runs. Use a self-verifiable JWT in Middleware and leave full session hydration to the route. Large npm imports increase cold-start time for every Middleware instance — keep middleware.ts dependencies to a lightweight JWT library and nothing else avoidable. Network-dependent rate limiting turns a Redis timeout into a timeout on every matched request; put rate limiting in a dedicated Route Handler.
FAQ
**Q: Does Middleware run on static file requests?**
A: Only on routes matching config.matcher. Without a matcher it runs on everything including _next/static files. The standard negative lookahead pattern excludes static assets.
**Q: Can we query a database from Next.js Middleware?**
A: Technically yes, but every matched request pays the round-trip. Use a self-contained JWT for Middleware verification; leave DB calls to the route or Server Component.
**Q: What is the difference between NextResponse.redirect and NextResponse.rewrite?**
A: redirect sends a 307 or 308 and the browser navigates to the new URL — the address bar updates. rewrite serves different content while keeping the original URL in the address bar. Auth flows use redirect; A/B tests and path aliases use rewrite.
**Q: Can Middleware read the POST request body?**
A: No — buffering the body breaks streaming and prevents the route handler from reading it. Base auth and routing decisions on cookies, headers, and URL only.
**Q: Is the Edge Runtime still required for Middleware in 2026?**
A: Not on Vercel. Middleware now runs on Fluid Compute (standard Node.js). The practical constraint is latency budget, not API availability.
Further Reading
Frontend Engineering
Shipping Bilingual Next.js Apps in English and Arabic: Routing, RTL, and What Crawlers Actually See
We build almost every product in English and Arabic. Field notes from our team: why cookie-based language switching hides half your site from crawlers, URL-per-locale routing in the App Router, RTL layouts with CSS logical properties, and hreflang done right.
Frontend Engineering
Route Handlers vs Server Actions in Next.js: When Our Team Reaches for Each
Server Actions and Route Handlers both run server code in the Next.js App Router, but they solve different problems. At Devya we use Server Actions for mutations from our own UI and Route Handlers for public APIs, webhooks, and streaming — and we treat both as public endpoints.