Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NextJs 14 middleware redirect issue: User Keeps getting redirected to /login after successful Sign-In #59218

Open
1 task done
anni1236012 opened this issue Dec 3, 2023 · 34 comments · May be fixed by #59931
Open
1 task done
Labels
bug Issue was opened via the bug report template. Runtime Related to Node.js or Edge Runtime with Next.js.

Comments

@anni1236012
Copy link

Link to the code that reproduces this issue

https://github.com/anni1236012/nextjsMiddleware

To Reproduce

  1. yarn dev
  2. All pages are protected via middleware except home page.
  3. Click on any link other than home page and it should redirect you to /login.
  4. Now click on login and it will set the sessionToken cookie with value "loggedin". At this stage, user is authenticated and it should allow users to see the protected pages.
  5. Now click on any protected page and you will see the /login page which is not the expected behavior. It should show the protected page immediately after the successful signin. However, it does work after a few seconds and user can see the protected page. I don't know why there is a delay.
nextjs_middleware_issue.mp4

Current vs. Expected behavior

Delayed navigation, looks like all requests are not reaching middleware and served from cache.

Verify canary release

  • I verified that the issue exists in the latest Next.js canary release

Provide environment information

Operating System:
  Platform: linux
  Arch: x64
  Version: #38~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Thu Nov  2 18:01:13 UTC 2
Binaries:
  Node: 20.5.1
  npm: 9.8.0
  Yarn: 1.22.19
  pnpm: N/A
Relevant Packages:
  next: 14.0.4-canary.37
  eslint-config-next: 14.0.3
  react: 18.2.0
  react-dom: 18.2.0
  typescript: 5.3.2
Next.js Config:
  output: N/A

Which area(s) are affected? (Select all that apply)

Middleware / Edge (API routes, runtime)

Additional context

No response

@anni1236012 anni1236012 added the bug Issue was opened via the bug report template. label Dec 3, 2023
@github-actions github-actions bot added the Runtime Related to Node.js or Edge Runtime with Next.js. label Dec 3, 2023
@SuhelMakkad
Copy link

@anni1236012 try setting the status code to 303. One of the reasons it might fail is because of the browser cache.
By default NextResponse.redirect will do a 307, which can be cahced by the browser.

return NextResponse.redirect(new URL("/login", request.url), {
    status: 303,
});

@anni1236012
Copy link
Author

@SuhelMakkad it did not change the behavior.

@SuhelMakkad
Copy link

Interesting 🤔 I am using this exact setup for my app and it works fine. I tried your repo on my local machine, and after the changes it worked. Maybe you need to clear your browser cache.

@yasermazlum
Copy link

I have exactly the same problem, it does not give the expected behaviour for a while

@anni1236012
Copy link
Author

I fixed it with rewrite instead of redirect.

return NextResponse.rewrite(new URL("/login", request.url), {
        status: 303,
      });

@yasermazlum
Copy link

@anni1236012 no, the problem is not solved. a short time still does not give the expected result

@anni1236012 anni1236012 reopened this Dec 7, 2023
@anni1236012
Copy link
Author

@yasermazlum Please share your github link to recreate the issue.

@babpulss
Copy link

try to avoid loading cached page
append random query on querystring
ex) localhost:3000/hello=${Date.now()}

@cyb3rsalih
Copy link

I also stucked at login page, when I debug see that The user object found and returns true response. But the status code is 303

@pedroSoaresll
Copy link

pedroSoaresll commented Jun 21, 2024

At NextJS 15 (canary version) it was fixed, the server middleware is correct changing the browser URL after NextResponse.redirect.

But I guess I found another error when passing a hash parameter /some-url#value-here, the hash parameter is not sending with the redirected URL.

The problem was not completely fixed.

@skychavda
Copy link

I am experiencing an issue with the middleware. I need to route the user based on their role, but the middleware is not able to route properly. Here's the explanation:

I have two roles in my app, manager and staff, and a user can have either of these roles in their respective org.

When the user changes the org using a dropdown in the UI, the middleware is supposed to detect the new selected org and route the user to the corresponding URL. I've written the following logic for this:

if (orgRole === 'manager' && request.nextUrl.pathname.includes('/staff')) {
      const url = request.nextUrl.clone()
      url.pathname = `${orgShortId}/dashboard/manager/app`
      return NextResponse.redirect(url, { status: 303 })
    }

    if (orgRole === 'staff' && request.nextUrl.pathname.includes('/manager')) {
      const url = request.nextUrl.clone()
      url.pathname = `${orgShortId}/dashboard/staff/app`
      return NextResponse.redirect(url, { status: 303 })
    }

However, the issue I'm facing is that the app is not getting routed to the URL as expected; it remains unchanged.

@JohnHuang-CT
Copy link

might need to use router.refresh

@Enzo-PVsyst
Copy link

Enzo-PVsyst commented Aug 6, 2024

I'm facing a similar issue, where in this middleware :

import { NextRequest, NextResponse } from "next/server";
import unauthorizedRoutes from "./utils/routes";
import evaluateTokensStatus from "./utils/evaluateTokens";

export async function middleware(request: NextRequest) {
  let tokensStatus = evaluateTokensStatus();

  const isAuthenticated =
    tokensStatus === "valid" || tokensStatus === "expired";

  const isUnauthorizedRoute = unauthorizedRoutes.some((route) =>
    request.nextUrl.pathname.startsWith(route)
  );

  console.log("isUnauthorizedRoute", isUnauthorizedRoute);
  console.log("isAuthenticated", isAuthenticated);

  if (!isAuthenticated && !isUnauthorizedRoute) {
    return NextResponse.redirect(new URL("/auth/login", request.url), {
      status: 303,
    });
  }

  if (isAuthenticated && isUnauthorizedRoute) {
    console.log("should redirect to /");
    return NextResponse.redirect(new URL("/", request.url), {
      status: 303,
    });
  }

  return NextResponse.next();
}

export const config = {
  matcher: ["/((?!api|_next/static|_next/image|.*\\.png$).*)"],
};

When redirect should happen it is not working, and nothing happens.

Strange thing ....

@avalanche-tm
Copy link

avalanche-tm commented Aug 7, 2024

Any solution to this? I have the same issue. NextResponse.redirect doesn't redirect but stays on the same site.
I'm using 14.3.0-canary.30, adding status 303 to redirect doesn't help either.

@skychavda
Copy link

I did one workaround though it is not a perfect solution but it worked fine in my case.

I have one drop-down through which I am changing the orgs in user accounts so when the user switches between orgs I add one line in the click event,

window.location.href = "/"

So here what happens is, the app sends the user to root and nextjs middleware executes the code as per my needs.

I hope this workaround helps you. ✌️

@Gamez0
Copy link

Gamez0 commented Sep 6, 2024

Workaround for all, Type redirectResponse.headers.set("x-middleware-cache", "no-cache"); to set x-middleware-cache to no-cache
Middleware isn't running due to cahce. Seems Vercel team is coming up with the fix but anyone can't wait plz check below.

import { type NextMiddlewareResult } from "next/dist/server/web/types";
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

export const middleware = async (request: NextRequest): Promise<NextMiddlewareResult> => {
  const cookieHeader = request.headers.get("cookie");
  const cookies = cookieHeader
    ? Object.fromEntries(cookieHeader.split("; ").map((c) => c.split("=")))
    : {};

  const accessToken = cookies["accessToken"];
  const userType = cookies["userType"];

  if (
    request.nextUrl.pathname.startsWith("/profile")
  ) {
    if (!accessToken) {
      const redirectResponse = NextResponse.redirect(new URL("/login", request.url));
      redirectResponse.headers.set("x-middleware-cache", "no-cache"); // Set x-middleware-cache to no-cache
      return redirectResponse;
    }
  }

  if (request.nextUrl.pathname.startsWith("/admin")) {
    if (userType !== "admin") {
      const redirectResponse = NextResponse.redirect(new URL("/", request.url));
      redirectResponse.headers.set("x-middleware-cache", "no-cache"); // Set x-middleware-cache to no-cache
      return redirectResponse;
    }
  }

  const response = NextResponse.next();
  response.headers.set(`x-middleware-cache`, `no-cache`); // Set x-middleware-cache to no-cache
  return response;
};

export const config = {
  matcher: ["/((?!api|_next/static|_next/image|favicon.ico|sitemap.xml|robots.txt).*)"],
};

@lpknv
Copy link

lpknv commented Sep 24, 2024

I'm facing a similar issue, where in this middleware :

import { NextRequest, NextResponse } from "next/server";
import unauthorizedRoutes from "./utils/routes";
import evaluateTokensStatus from "./utils/evaluateTokens";

export async function middleware(request: NextRequest) {
  let tokensStatus = evaluateTokensStatus();

  const isAuthenticated =
    tokensStatus === "valid" || tokensStatus === "expired";

  const isUnauthorizedRoute = unauthorizedRoutes.some((route) =>
    request.nextUrl.pathname.startsWith(route)
  );

  console.log("isUnauthorizedRoute", isUnauthorizedRoute);
  console.log("isAuthenticated", isAuthenticated);

  if (!isAuthenticated && !isUnauthorizedRoute) {
    return NextResponse.redirect(new URL("/auth/login", request.url), {
      status: 303,
    });
  }

  if (isAuthenticated && isUnauthorizedRoute) {
    console.log("should redirect to /");
    return NextResponse.redirect(new URL("/", request.url), {
      status: 303,
    });
  }

  return NextResponse.next();
}

export const config = {
  matcher: ["/((?!api|_next/static|_next/image|.*\\.png$).*)"],
};

When redirect should happen it is not working, and nothing happens.

Strange thing ....

I do have a similar issue following the official docs / guide https://nextjs.org/docs/app/building-your-application/authentication#optimistic-checks-with-middleware-optional. The redirect simply does not work as expected.

@Gamez0
Copy link

Gamez0 commented Sep 25, 2024

@lpknv Try disable middleware cache before you return response using
redirectResponse.headers.set("x-middleware-cache", "no-cache");

@lpknv
Copy link

lpknv commented Sep 25, 2024

@lpknv Try disable middleware cache before you return response using redirectResponse.headers.set("x-middleware-cache", "no-cache");

Thanks, I will give it a try!

@joshjh2002
Copy link

@lpknv Did that work? I am having the same issue trying to deploy to a digital ocean server

@lpknv
Copy link

lpknv commented Sep 30, 2024

"x-middleware-cache", "no-cache");

I didn't try it yet. If you are curious you can add redirectResponse.headers.set("x-middleware-cache", "no-cache"); to your middleware. I don't think it has something to do with digital ocean server.

@joshjh2002
Copy link

joshjh2002 commented Sep 30, 2024

@lpknv unfortunately it didn't work. I belive my issue stems from my SSL certificate which is described here. My SSL guy is sorting it out now.

https://vercel.com/guides/resolve-err-too-many-redirects-when-using-cloudflare-proxy-with-vercel

Edit: it was an SSL issue, as well as an API being redirected to the wrong route which caused a loop during production as the environment variable wasn't set properly

@Phreeshad
Copy link

I fixed it with rewrite instead of redirect.

return NextResponse.rewrite(new URL("/login", request.url), {
        status: 303,
      });

This might be because your use case only requires server-side. However, this doesn’t actually solve the issue. https://www.dhiwise.com/post/nextjs-rewrites-everything-you-need-to-know

@lubojanski
Copy link

for me the problem was that I was trying to make the middleware cleaner

export async function middleware(request: NextRequest) {

  test(request);

  return NextResponse.next();
}

function test(request: NextRequest) {
  return NextResponse.redirect(new URL('/someroute', request.url))
}

this didn't work until I just squashed everything into middleware function directly

@lpknv
Copy link

lpknv commented Oct 29, 2024

for me the problem was that I was trying to make the middleware cleaner

export async function middleware(request: NextRequest) {

  test(request);

  return NextResponse.next();
}

function test(request: NextRequest) {
  return NextResponse.redirect(new URL('/someroute', request.url))
}

this didn't work until I just squashed everything into middleware function directly

I can't recall that in the nextjs docs that you can use Middleware everywhere you want.

@toannguyenUITT
Copy link

`

export default async function middleware(req: NextRequest) {
const publicPathnameRegex = RegExp(
^(/(${locales.join('|')}))?(${publicRoutes .flatMap((p) => (p === '/' ? ['', '/'] : p)) .join('|')})/?$,
'i',
)
const isPublicPage = publicPathnameRegex.test(req.nextUrl.pathname)

if (isPublicPage) {
return intlMiddleware(req)
} else {
// return (authMiddleware as any)(req)

const token = await getToken({ req, secret: secret })

if (token && Math.floor(Date.now() / 1000) < (token.exp as number)) {
  // Token is valid, continue with the request
  return intlMiddleware(req)
}

return NextResponse.redirect(`http://localhost:3000`, 303)

}
}

export const config = {
// Skip all paths that should not be internationalized
matcher: ['/((?!api|_next|.\..).*)'],
}

`

Still stuck :((, the network tab show the post request to '/' (200 success) but redirect not perform ,

@sunipan
Copy link

sunipan commented Nov 5, 2024

@lubojanski

for me the problem was that I was trying to make the middleware cleaner

export async function middleware(request: NextRequest) {

  test(request);

  return NextResponse.next();
}

function test(request: NextRequest) {
  return NextResponse.redirect(new URL('/someroute', request.url))
}

this didn't work until I just squashed everything into middleware function directly

You're not returning your test function result in the middleware going straight to NextResponse.next()

@truongnv98

This comment has been minimized.

@joshjh2002
Copy link

@truongnv98

It probably isn't the same for you, but my issue was a combination of a mistyped environment variable name for the API route and my SSL certificate being invalid so it tried to route to a HTTP version

@lpknv
Copy link

lpknv commented Nov 18, 2024

@truongnv98

It probably isn't the same for you, but my issue was a combination of a mistyped environment variable name for the API route and my SSL certificate being invalid so it tried to route to a HTTP version

"mistyped environment variable name" - I can relate to that haha...

@MuhammadAzfarAslam
Copy link

MuhammadAzfarAslam commented Dec 10, 2024

I've tried all the solutions mentioned here, but nothing is working. Has anyone come up with a new solution?

I am using two redirects, 1st redirect from config and the second in middleware. 1st redirection is working fine, but following code is not working

const redirectUrl = new URL(
        `https://URL.com/${sport}/news/${resCall?.data?.redirect}`,
        request.url
      );

return NextResponse.redirect(redirectUrl, {
        status: 308,
      });
      

@HCarrer
Copy link

HCarrer commented Dec 26, 2024

Same problem here. Tried disabling cache, adding 303 status code, changing from redirect to rewrite, everything mentioned here and still didnt get it to work.

@adrianomolin
Copy link

@HCarrer did you manage to solve it in any way?

@daveycodez
Copy link

daveycodez commented Jan 27, 2025

Need to manually call router.refresh() in the client I think. To clear the router cache. That was my solution for fixing middleware after user's log in. Refreshing the page fixed it, but router.refresh() does also. I wish there was a way to delete the router cache without reloading the page though.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Issue was opened via the bug report template. Runtime Related to Node.js or Edge Runtime with Next.js.
Projects
None yet
Development

Successfully merging a pull request may close this issue.