The Invisible 307 Middleware Send Behind a Login-Dashboard-Login Loop

After a successful login, the page loops back. No console error. No logged exception. The middleware said everything — but not where you were looking.

A circular loop of three content cards with one arrow highlighted in blue, illustrating a redirect cycle

The user logs in. The browser navigates to /dashboard. The dashboard page does not render. The browser is back on /login, with no error message, no console output, no network failure visible in the default DevTools view.

In a PHP application, this scenario prints a stack trace or logs a meaningful exception. Symfony's security component throws an AccessDeniedException that you can catch, log, and trace to its source. Laravel's middleware returns a clear RedirectResponse that you can inspect. There is always a signal.

In Next.js, the signal exists, but it is not in the console. It is in the Network tab.

Why the browser sees nothing

Next.js middleware runs in the Edge Runtime, on the server, before the request reaches any React component. It does not execute JavaScript in the browser context. When the middleware decides to reject a request, for example because the JWT is invalid or expired, it sends an HTTP 307 Temporary Redirect back to /login.

The browser follows the redirect silently. There is no JavaScript involved, so there is no JavaScript error. The page renders /login as if that was the intended destination all along.

js
Browser sends:  GET /dashboard
Middleware:     checks JWT → invalid → HTTP 307Location: /login
Browser:        follows the redirect
Result:         /login renders, console shows nothing

The PHP mental model

In PHP, a security middleware runs inside the same request-response cycle as your application code. Symfony voters, for example, are invoked during controller resolution and throw an AccessDeniedException that propagates through the normal exception-handling pipeline. You can log it, trace it, and handle it with custom error pages.

The concept of a "middleware that runs before the framework itself", where a redirect can happen without your application code ever executing, does not exist in the same form. In Next.js, the middleware is a separate layer with its own runtime, its own configuration, and its own failure modes that are invisible to the React component tree.

How to diagnose

Option 1: Network tab with Preserve Log

Enable "Preserve log" in DevTools before triggering the login. After the redirect loop, look for a request to /dashboard with status 307. This confirms the middleware intercepted the navigation.

Option 2: Container or server logs

bash
docker logs app --tail=50

Output:

js
POST /api/auth/set-cookie 200
GET  /dashboard 307
GET  /login 200

The 307 on /dashboard is the middleware. The 200 on /login is the follow. The pattern tells you the middleware rejected the JWT. Look at the JWT configuration, not the component code.

Common root causes

  • JWT key mismatch: the public key in the Next.js project does not match the private key used by the backend to sign the token.
  • Expired JWT: the exp claim is in the past, or the system clock is wrong.
  • Wrong cookie name: the middleware expects a cookie named jwt but the login action stores it under a different name.

None of these produce a JavaScript error. All of them produce the same symptom: login → dashboard → login, with nothing in the console.


Delaa