Next.js Middleware: A Developer’s Guide with Real Use Cases and Code Examples
Next.js has become the go-to React framework for building production-grade web applications. With features like file-based routing, API routes, and server-side rendering, it offers developers speed and flexibility. But one feature often underutilized (or misunderstood) is Next.js Middleware.
Next.js has become the go-to React framework for building production-grade web applications. With features like file-based routing, API routes, and server-side rendering, it offers developers speed and flexibility. But one feature often underutilized (or misunderstood) is Next.js Middleware.
Middleware enables powerful edge logic that executes before a request is completed—allowing developers to intercept, modify, or redirect requests in near real-time. In this article, we’ll explore what Next.js Middleware is, when to use it, and walk through concrete code examples and real-world use cases.
What is Next.js Middleware?
Middleware in Next.js is a function that runs before a request is processed by your app’s routes or APIs. It lives at the edge layer, meaning closer to the user, reducing latency and allowing for smarter request handling.
Key characteristics:
- Runs on the Edge Runtime (based on V8, not Node.js).
- Executes before rendering or API logic.
- Can rewrite, redirect, modify headers, or return responses.
- Runs in a stateless, lightweight environment, making it blazing fast.
File location:
/middleware.ts # or middleware.js
Example boilerplate:
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
// Example: Block access to /admin if not logged in
if (
!request.cookies.get("auth-token") &&
request.nextUrl.pathname.startsWith("/admin")
) {
return NextResponse.redirect(new URL("/login", request.url));
}
return NextResponse.next();
}
Why Use Middleware?
Think of Middleware as your bouncer at the door—it decides what happens before the request enters your app. Some common scenarios:
- Authentication & Authorization
- Protect private routes.
- Redirect unauthenticated users to login.
- A/B Testing & Personalization
- Show different experiences to users based on cookies, headers, or geolocation.
- Localization
- Detect user’s region or language and redirect accordingly.
- Bot Detection & Rate Limiting
- Block suspicious requests early.
- Analytics & Logging
- Capture request metadata without touching route logic.
Practical Use Cases with Code
1. Protecting Routes with Authentication
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
export function middleware(request: NextRequest) {
const token = request.cookies.get("token");
if (!token && request.nextUrl.pathname.startsWith("/dashboard")) {
return NextResponse.redirect(new URL("/login", request.url));
}
return NextResponse.next();
}
👉 Useful for dashboards, admin panels, or member-only content.
2. Internationalization (i18n) Redirects
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
const locale = request.headers.get("accept-language")?.split(",")[0] || "en";
if (pathname === "/") {
return NextResponse.redirect(new URL(`/${locale}`, request.url));
}
}
👉 Detects browser language and redirects to localized content (/en, /fr, etc.).
3. A/B Testing or Feature Flags
export function middleware(request: NextRequest) {
const group = Math.random() < 0.5 ? "A" : "B";
if (group === "A") {
return NextResponse.rewrite(new URL("/experiment/a", request.url));
}
return NextResponse.rewrite(new URL("/experiment/b", request.url));
}
👉 Split traffic dynamically at the edge without complex setup.
4. Blocking Bots and Bad Actors
const blockedAgents = ["curl", "python", "scrapy"];
export function middleware(request: NextRequest) {
const ua = request.headers.get("user-agent")?.toLowerCase() || "";
if (blockedAgents.some((agent) => ua.includes(agent))) {
return new Response("Blocked", { status: 403 });
}
return NextResponse.next();
}
👉 Prevent scrapers or unwanted crawlers from hammering your app.
Performance Considerations
- Runs at the Edge: Middleware is executed globally at CDN-level, so it’s fast.
- Stateless: No fs, crypto, or Node.js APIs. Use Web APIs only.
- Bundle Size Matters: Keep middleware lightweight—avoid large dependencies.
- Chaining Middleware: You can conditionally run multiple checks, but ensure minimal overhead.
Best Practices
- Place logic only where needed—don’t run heavy computation.
- Combine middleware with cookies or headers for personalization.
- Use NextResponse.rewrite for internal rewrites and NextResponse.redirect for external navigation.
- Always have a fallback path to avoid infinite loops.
SEO Benefits of Middleware
Middleware indirectly helps SEO by:
- Improving page load speed (early redirects reduce TTFB).
- Supporting localized content (serving language-specific pages).
- Enforcing canonical routes (redirect duplicates).
- Blocking spam/bots, protecting crawl budget.
Conclusion
Next.js Middleware is more than just a request filter—it’s a powerful edge tool that lets developers build secure, performant, and personalized apps without bloating client or server logic.
Whether you’re handling authentication, localization, A/B testing, or bot protection, Middleware should be in your Next.js toolbox.
With great power comes great responsibility: keep your middleware fast, simple, and focused.
✅ Pro Tip: Combine Middleware with Edge API Routes for even more dynamic edge-driven apps.
🤝 Need a Custom RSVP System or Dashboard?
I help businesses build tools that actually work , even on tight deadlines.
Whether you're planning an event, need internal tools, or want a custom dashboard for your team , I can help.
Reach out
📧 Email: safi.abdulkader@gmail.com | 💻 LinkedIn: @abdulkader-safi | 📱 Instagram: @abdulkader.safi | 🏢 DSRPT
Drop me a line, I’m always happy to collaborate! 🚀
Building scalable systems and developer-first tools. Lead Software Engineer at DSRPT.
Related articles
Frequently asked
-
Next.js middleware is a function that runs before a request is processed by your app's routes or APIs, executing at the edge layer closer to the user to reduce latency. It can rewrite, redirect, modify headers, or return responses entirely, all before any rendering or API logic runs. You define it in a single middleware.ts or middleware.js file at the root of your project.
-
Common use cases include authentication and authorization to protect private routes and redirect unauthenticated users to login, internationalization to detect a user's language and redirect to localized content, and A/B testing or feature flags to split traffic at the edge. It is also used for bot detection and rate limiting to block suspicious requests early, and for capturing analytics or logging request metadata without touching route logic.
-
You read a cookie such as an auth token from the incoming request and check whether the requested path starts with a protected prefix like /dashboard or /admin. If the token is missing on a protected path, you return NextResponse.redirect to send the user to the login page, otherwise you call NextResponse.next to let the request continue. This pattern is ideal for dashboards, admin panels, and member-only content.
-
In the middleware described here, no. It runs on the Edge Runtime, which is based on V8 rather than Node.js, so it is stateless and lightweight and you must use Web APIs instead of Node modules like fs or crypto. Because of this you should keep middleware small, avoid large dependencies, and place logic only where needed to keep it fast.
-
NextResponse.rewrite serves content from a different internal path while keeping the original URL in the browser, which is useful for things like A/B testing or routing to experiment pages without changing what the user sees. NextResponse.redirect actually sends the user to a new URL, which is appropriate for navigation like sending unauthenticated users to a login page or redirecting to a localized route. A best practice is to always provide a fallback path to avoid infinite redirect loops.