next.js: [NEXT-1088] After upgrading to 13.3 from 13.2 middleware is not working anymore

Verify canary release

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

EDIT: Downgraded to 13.2.4 and it is working again, what could be the issue?

This is my middleware and when I click in my client login component the login button, in the network tab login is pending and doesn’t do anything, when i rename middleware.js to middleware.bak and I retry it works, and it was working fine before I updated to 13.3

I hope its a bug or something changed

I’m using the app folder

This is my code:

Provide environment information


import { NextResponse } from 'next/server';

export const config = {
  // matcher solution for public, api, assets and _next exclusion
  matcher: '/((?!api|static|.*\\..*|_next).*)',
};

const maintenance = 'false';

function setAccessTokenCookie(response, accessToken) {
  response.cookies.set('access', accessToken, {
    httpOnly: true,
    maxAge: 10 * 60 * 10, // 10 minutes
  });

  return response;
}

async function refreshAccessToken(request, origin) {
  const refreshCookie = request.cookies.get('refresh')?.value;
  if (!refreshCookie) return null;

  const res = await fetch(process.env.BASE_URL + `/auth/refresh`, {
    headers: { refresh: refreshCookie },
  });

  if (!res.ok) return null;

  const { accessToken } = await res.json();

  return accessToken;
}

async function verifyAccessToken(request, origin) {
  const accessCookie = request.cookies.get('access')?.value;
  const refreshCookie = request.cookies.get('refresh')?.value;

  if (!accessCookie && !refreshCookie) return null;

  const res = await fetch(process.env.BASE_URL + `/auth/verify`, {
    headers: { access: accessCookie, refresh: refreshCookie },
  });

  if (!res.ok) return null;

  const { success } = await res.json();

  return success ?? null;
}

function clearAuthCookies(response) {
  response.cookies.set('auth', 'false');
  response.cookies.set('access', '', {
    httpOnly: true,
    expires: new Date(0),
  });
  response.cookies.set('refresh', '', {
    httpOnly: true,
    expires: new Date(0),
  });
  return response;
}

export async function middleware(request) {
  const { method, pathname, origin } = request.nextUrl;

  if (maintenance === 'true' && pathname !== '/maintenance') {
    return NextResponse.redirect(process.env.SITE_URL + `/maintenance`);
  } else if (maintenance === 'false' && pathname == '/maintenance') {
    return NextResponse.redirect(process.env.SITE_URL + `/`);
  }

  const response = NextResponse.next();
  const refreshCookie = request.cookies.get('refresh')?.value;
  const accessCookie = request.cookies.get('access')?.value;

  let user = await verifyAccessToken(request, origin);

  if (user && refreshCookie) {
    if (pathname.startsWith('/auth/')) {
      response.cookies.set('auth', 'true');
      return NextResponse.redirect(process.env.SITE_URL + `/app/dashboard`);
    }
    response.cookies.set('auth', 'true');
    return NextResponse.next(response);
  } else if (user && !refreshCookie) {
    clearAuthCookies(response);
    if (pathname.startsWith('/app/')) {
      return NextResponse.redirect(process.env.SITE_URL + `/auth/login`);
    }
    return NextResponse.next(response);
  } else if (!user && refreshCookie) {
    let newAccess = await refreshAccessToken(request, origin);

    if (newAccess) {
      try {
        setAccessTokenCookie(response, newAccess);
      } catch (error) {
        clearAuthCookies(response);
        if (pathname.startsWith('/app/')) {
          return NextResponse.redirect(process.env.SITE_URL + `/auth/login`);
        }
        return NextResponse.next(response);
      }
      if (pathname === '/auth/login') {
        response.cookies.set('auth', 'true');
        return NextResponse.redirect(process.env.SITE_URL + `/app/dashboard`);
      }
      return response;
    } else if (!newAccess) {
      clearAuthCookies(response);
      if (pathname.startsWith('/app/')) {
        return NextResponse.redirect(process.env.SITE_URL + `/auth/login`);
      }
      return NextResponse.next(response);
    }
  } else if (!user && !refreshCookie) {
    clearAuthCookies(response);
    if (pathname.startsWith('/app/')) {
      return NextResponse.redirect(process.env.SITE_URL + `/auth/login`);
    }
    response.cookies.set('auth', 'false');
    return NextResponse.next(response);
  }
}

Which area(s) of Next.js are affected? (leave empty if unsure)

App directory (appDir: true), Middleware / Edge (API routes, runtime)

Link to the code that reproduces this issue

N/A

To Reproduce

Upgraded from 13.2 to 13.3

Describe the Bug

My middleware is not working which was working completely fine before update

Expected Behavior

I should be sent to the dashboard page after clicking the login button

Which browser are you using? (if relevant)

No response

How are you deploying your application? (if relevant)

No response

NEXT-1088

About this issue

  • Original URL
  • State: open
  • Created a year ago
  • Reactions: 10
  • Comments: 28 (5 by maintainers)

Most upvoted comments

Just to add here because it took me a while to figure it out: if you overrode the pageExtensions in next.config.js you also need to add the corresponding extension to the middleware file. In my case, I had to use src/middleware.page.tsx:

  pageExtensions: ['page.tsx', 'page.ts'],

PS: I’m using the pages/ folder

Moving middleware.ts inside the /repo worked for me. 🙆

  • repo/
    • middleware.ts
    • app/

Moving middleware.ts inside the /app NOT worked for me. 🙅🏻‍♂️

  • repo/
    • app/
      • middleware.ts

This is version 13.4.12.

It’s already in /src folder for me, didn’t help, perhaps its something else.

Moving middleware.ts inside the /src worked for me.

My Versions:

  • next: “13.3.0”,
  • next-auth: “^4.22.0”,

Just for clarification. The middleware needs to be at the same level of your /app directory.

- /src
- - /app
- - middleware.ts
- /app
- middleware.ts

/root
- /src
- - /app
- middleware.ts

Seeing this too…

We’re seeing this too, this is our middleware.ts file:

import { NextRequest, NextResponse } from 'next/server';

export const config = {
  matcher: [
    /*
     * Match all request paths except for the ones starting with:
     * - api (API routes)
     * - _next/static (static files)
     * - _next/image (image optimization files)
     * - favicon.ico (favicon file)
     */
    '/((?!api|_next/static|_next/image|favicon.ico).*)'
  ]
};

export function middleware(req: NextRequest) {
  const basicAuth = req.headers.get('Authorization');

  if (basicAuth) {
    const authValue = basicAuth.split(' ')[1];
    // atob is deprecated but Buffer.from is not available in Next.js edge.
    const [user, password] = atob(authValue).split(':');

    if (user === 'XXX' && password === 'XXX') {
      return NextResponse.next();
    }

    return NextResponse.json(
      { error: 'Invalid credentials' },
      { headers: { 'WWW-Authenticate': 'Basic realm="XXX"' }, status: 401 }
    );
  } else {
    return NextResponse.json(
      { error: 'Please enter credentials' },
      { headers: { 'WWW-Authenticate': 'Basic realm="XXX"' }, status: 401 }
    );
  }
}

same here

The fix for me was that I was using both pages and app router simultaneously during incremental adoption:

  • repo
    • pages - REMOVING THIS FIXED IT
    • src
      • app
      • middleware.ts

This is version 13.4.16.

image

▲ Next.js 13.5.4

  • /src
    • /app
    • middleware.ts

I am having this issue, even using 13.4.19.

It’s quite weird. Anytime I use the config with matcher, the pages just blow up with an error. It works for a while when I use conditional statements to match certain routes but it still blows up after.

Sometimes it works alright for a while and then blows up immediately I make changes and at other times too it blows up when I change to async method, it’s just intermittent and blows up in the middleware with the error message below:

This is the client error I keep seeing:

Server Error
Error: Could not find the module "/home/user/project/node_modules/next/dist/client/components/error-boundary.js#" in the React Client Manifest. This is probably a bug in the React Server Components bundler.

Call Stack
resolveClientReferenceMetadata
node_modules/next/dist/compiled/react-server-dom-webpack-experimental/cjs/react-server-dom-webpack-server.edge.development.js (402:12)
resolveClientReferenceMetadata
node_modules/next/dist/compiled/react-server-dom-webpack-experimental/cjs/react-server-dom-webpack-server.edge.development.js (1803:34)
serializeClientReference
node_modules/next/dist/compiled/react-server-dom-webpack-experimental/cjs/react-server-dom-webpack-server.edge.development.js (2181:13)
resolveModelToJSON
node_modules/next/dist/compiled/react-server-dom-webpack-experimental/cjs/react-server-dom-webpack-server.edge.development.js (1412:13)
stringify
<anonymous>
stringify
node_modules/next/dist/compiled/react-server-dom-webpack-experimental/cjs/react-server-dom-webpack-server.edge.development.js (2635:13)
processModelChunk
node_modules/next/dist/compiled/react-server-dom-webpack-experimental/cjs/react-server-dom-webpack-server.edge.development.js (2341:25)
retryTask
node_modules/next/dist/compiled/react-server-dom-webpack-experimental/cjs/react-server-dom-webpack-server.edge.development.js (2388:6)
performWork
node_modules/next/dist/compiled/react-server-dom-webpack-experimental/cjs/react-server-dom-webpack-server.edge.development.js (1707:13)
listOnTimeout
node:internal/timers (569:17)
process.processTimers
node:internal/timers (512:7)

Same problem on 13.4.5