A Next.js middleware redirect loop often happens when a redirect sends a request to a path that the same middleware protects. The browser follows the redirect, the middleware runs again, and the cycle repeats. This guide shows how to identify that pattern, scope route matching safely, and handle authentication redirects without treating a cookie check as full authorization.
Key Takeaways
- A login redirect loops if the login path is also redirected as an unauthenticated protected page.
- Scope Proxy or Middleware to routes that actually need the check; explicit protected-route matchers are often the clearest option.
- If using a broad matcher, exclude login and static resources deliberately, and avoid excluding API routes that need protection.
- Treat a cookie check as a routing hint, not proof that a session is valid. Enforce authorization in the relevant server-side code too.
Why the redirect loop happens
Suppose an unauthenticated visitor requests /dashboard. The middleware redirects them to /login. If the middleware also handles /login and redirects every unauthenticated request there, the browser receives another redirect to /login, then another, indefinitely.
The browser may eventually report “too many redirects.” In the Network panel, repeated responses will usually show the same redirect status and a Location header pointing to the same URL or cycling between two URLs. A loop can also arise when one redirect adds or removes a trailing slash, locale prefix, or query parameter and another redirect reverses that change.
Check the matcher and redirect destination
Start by finding the Proxy or Middleware entry point and inspecting its config.matcher. Then follow the redirect destination: is it matched again, and does the code make a different decision on that next request?
For authentication redirects, check these cases:
- An unauthenticated request to a protected page should redirect to the login page.
- An unauthenticated request to the login page should be allowed through.
- An authenticated request should not be redirected back to login by a stale or incorrect condition.
- The login page should not redirect to a destination that immediately sends the visitor back to login.
Also inspect the actual request path and query string. Locale or trailing-slash redirects may be configured separately from authentication, so the loop can involve more than one redirect rule.
Use the correct file convention for your Next.js version
In Next.js 16, the convention is proxy.ts and the exported function is named proxy. In earlier versions that use Middleware, the convention is middleware.ts and the exported function is named middleware. Use the convention supported by the version installed in your project; don’t define both as a workaround.
Corrected authentication example
This Next.js 16 example matches only the protected page areas. Because /login and static assets are outside those matchers, they cannot be sent through this redirect check. The example assumes the application has a session cookie; checking that it exists is only a simple routing illustration, not session validation.
// proxy.ts
import { NextRequest, NextResponse } from 'next/server'
export function proxy(request: NextRequest) {
const session = request.cookies.get('session')?.value
// A cookie-presence check is not sufficient authorization.
// Validate the session in trusted server-side code as well.
if (session) {
return NextResponse.next()
}
const loginUrl = new URL('/login', request.url)
const returnTo = request.nextUrl.pathname + request.nextUrl.search
loginUrl.searchParams.set('next', returnTo)
return NextResponse.redirect(loginUrl)
}
export const config = {
matcher: ['/dashboard/:path*', '/account/:path*'],
}
For an unauthenticated request to /dashboard?tab=recent, the redirect goes to /login?next=%2Fdashboard%3Ftab%3Drecent. The login route is not matched by this configuration, so the redirect check does not redirect the visitor to itself. Routes such as /about, /login, and files served from Next.js static paths are also outside the two protected matchers.
If you need a broad matcher instead, explicitly account for public routes and static resources, and ensure the code passes them through. Do not exclude every API path by default: API endpoints that require authentication still need their own authorization checks. A matcher controls where Proxy runs; it does not secure routes that are outside its scope.
Handle the return destination safely
The next query parameter is untrusted input. Before using it after login, accept only a local path. For example, reject absolute URLs and paths beginning with //; otherwise, a crafted value could send the user to an external site. Also ensure the destination is appropriate for the signed-in user. A safe redirect destination does not replace authorization on that destination.
Do not rely on Proxy alone to establish that the cookie represents a valid, authorized session. Validate the session and permissions in the server-side page, data-access layer, or API handler that performs the protected operation. This matters because route matching and client-side navigation are not substitutes for access control.
Common Mistakes
- Redirecting every unauthenticated request to login: The login route itself then triggers the same redirect. Exempt it or, preferably, match only protected routes.
- Using an overly broad matcher: A rule that catches every page can unexpectedly include login, public pages, and resources needed to render the login screen.
- Excluding all API routes: This can leave API endpoints without the Proxy check you expected. Authenticate APIs at their server-side boundary.
- Trusting cookie presence: A present cookie can be expired, malformed, or invalid. Validate it and authorize access where protected data is used.
- Creating a redirect cycle with
next: Validate the return path and test what happens after login, including when the destination is itself inaccessible. - Assuming a matcher is an authorization rule: A matcher determines which requests run through Proxy; it does not grant or deny access by itself.
Best Practices
- Prefer an explicit list of protected route patterns when the protected area is small and well-defined.
- Keep the login route and required static resources reachable without the authentication redirect.
- Test unauthenticated and authenticated requests to both the protected route and login route. Check status codes and
Locationheaders, not only the browser’s final error. - Use a bounded redirect-following check in local or staging tests, so repeated redirects fail clearly rather than hanging.
- Keep authoritative session validation and permission checks close to the server-side operation they protect.
- When investigating, temporarily log the pathname and redirect destination in a safe development environment; never log session secrets.
Conclusion
A Next.js middleware redirect loop is usually a route-matching problem: the redirect target is caught by the same rule that produced the redirect. Scope the matcher to protected paths, allow the login flow to proceed, validate return destinations, and enforce authorization on the server.
References
- Next.js Proxy file convention — current Proxy convention and matcher configuration.
- Next.js Middleware documentation for version 15 — Middleware convention for projects using Next.js 15.
