[GH-ISSUE #2170] getSessionCookie Returns null in Next.js 15.2 Edge Runtime #9079

Closed
opened 2026-04-13 04:22:54 -05:00 by GiteaMirror · 9 comments
Owner

Originally created by @camunoz2 on GitHub (Apr 7, 2025).
Original GitHub issue: https://github.com/better-auth/better-auth/issues/2170

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

  1. Set up a Next.js 15.2.1 project with better-auth version 1.2.3.​
  2. Implement the middleware.ts as follows:
import { NextRequest, NextResponse } from "next/server";
import { getSessionCookie } from "better-auth/cookies";

export function middleware(request: NextRequest) {
  const session = getSessionCookie(request, {
    cookieName: "session_token",
    cookiePrefix: "better-auth",
  });

  if (!session) {
    return NextResponse.redirect(new URL("/unauthorized", request.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: ["/dashboard/:path*"],
};
  1. Ensure that the session cookie better-auth.session_token is set in the browser and sent with requests to /dashboard
  2. Access the /dashboard route

Current vs. Expected behavior

Expected Behavior: The middleware should detect the session cookie and allow access to the /dashboard route.​
Actual Behavior: The getSessionCookie function returns null, resulting in a redirect to the /unauthorized page.​

What version of Better Auth are you using?

1.2.3​

Provide environment information

- OS: Windows 11
- Browser Zen Browser (Firefox)

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

Backend

Auth config (if applicable)

import { betterAuth } from "better-auth";
import { admin } from "better-auth/plugins";
import { drizzleAdapter } from "better-auth/adapters/drizzle";
import { db } from "@/lib/drizzle";
import { schema } from "@/db/schema/auth-schema";
import { nextCookies } from "better-auth/next-js";

export const auth = betterAuth({
  user: {
    additionalFields: {
      schoolId: {
        type: "number",
      },
    },
  },
  database: drizzleAdapter(db, {
    provider: "sqlite",
    schema: {
      ...schema,
    },
  }),
  plugins: [
    admin({
      requireRole: true,
      defaultRole: "unauthorized",
      adminRole: "admin",
      allowedRoles: ["admin", "user"],
      api: {
        enabled: true,
      },
    }),
    nextCookies(),
  ],
  socialProviders: {
    google: {
      clientId: process.env.GOOGLE_CLIENT_ID as string,
      clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
    },
  },
});

export const { api } = auth;

export type Session = typeof auth.$Infer.Session;

Additional context

Logging request.cookies.getAll() within the middleware confirms the presence of the better-auth.session_token cookie.​

Manually retrieving the cookie using request.cookies.get("better-auth.session_token") works as expected, suggesting an issue with getSessionCookie in the Edge Runtime environment.​

The better-auth documentation indicates that getSessionCookie should function in middleware by matching the cookieName and cookiePrefix to the authentication configuration.​

Originally created by @camunoz2 on GitHub (Apr 7, 2025). Original GitHub issue: https://github.com/better-auth/better-auth/issues/2170 ### Is this suited for github? - [x] Yes, this is suited for github ### To Reproduce 1. Set up a Next.js 15.2.1 project with better-auth version 1.2.3.​ 2. Implement the middleware.ts as follows: ```ts import { NextRequest, NextResponse } from "next/server"; import { getSessionCookie } from "better-auth/cookies"; export function middleware(request: NextRequest) { const session = getSessionCookie(request, { cookieName: "session_token", cookiePrefix: "better-auth", }); if (!session) { return NextResponse.redirect(new URL("/unauthorized", request.url)); } return NextResponse.next(); } export const config = { matcher: ["/dashboard/:path*"], }; ```` 3. Ensure that the session cookie better-auth.session_token is set in the browser and sent with requests to /dashboard 4. Access the /dashboard route ### Current vs. Expected behavior Expected Behavior: The middleware should detect the session cookie and allow access to the /dashboard route.​ Actual Behavior: The getSessionCookie function returns null, resulting in a redirect to the /unauthorized page.​ ### What version of Better Auth are you using? 1.2.3​ ### Provide environment information ```bash - OS: Windows 11 - Browser Zen Browser (Firefox) ``` ### Which area(s) are affected? (Select all that apply) Backend ### Auth config (if applicable) ```typescript import { betterAuth } from "better-auth"; import { admin } from "better-auth/plugins"; import { drizzleAdapter } from "better-auth/adapters/drizzle"; import { db } from "@/lib/drizzle"; import { schema } from "@/db/schema/auth-schema"; import { nextCookies } from "better-auth/next-js"; export const auth = betterAuth({ user: { additionalFields: { schoolId: { type: "number", }, }, }, database: drizzleAdapter(db, { provider: "sqlite", schema: { ...schema, }, }), plugins: [ admin({ requireRole: true, defaultRole: "unauthorized", adminRole: "admin", allowedRoles: ["admin", "user"], api: { enabled: true, }, }), nextCookies(), ], socialProviders: { google: { clientId: process.env.GOOGLE_CLIENT_ID as string, clientSecret: process.env.GOOGLE_CLIENT_SECRET as string, }, }, }); export const { api } = auth; export type Session = typeof auth.$Infer.Session; ``` ### Additional context Logging request.cookies.getAll() within the middleware confirms the presence of the better-auth.session_token cookie.​ Manually retrieving the cookie using request.cookies.get("better-auth.session_token") works as expected, suggesting an issue with getSessionCookie in the Edge Runtime environment.​ The better-auth documentation indicates that getSessionCookie should function in middleware by matching the cookieName and cookiePrefix to the authentication configuration.​
GiteaMirror added the locked label 2026-04-13 04:22:54 -05:00
Author
Owner

@camunoz2 commented on GitHub (Apr 7, 2025):

I solved it recently like this

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

export function middleware(request: NextRequest) {
  const cookie = request.cookies.get("better-auth.session_token");

  if (!cookie || !cookie.value) {
    return NextResponse.redirect(new URL("/unauthorized", request.url));
  }

  return NextResponse.next();
}

export const config = {
  matcher: ["/dashboard/:path*"],
};
<!-- gh-comment-id:2784885718 --> @camunoz2 commented on GitHub (Apr 7, 2025): I solved it recently like this ```ts import { NextRequest, NextResponse } from "next/server"; export function middleware(request: NextRequest) { const cookie = request.cookies.get("better-auth.session_token"); if (!cookie || !cookie.value) { return NextResponse.redirect(new URL("/unauthorized", request.url)); } return NextResponse.next(); } export const config = { matcher: ["/dashboard/:path*"], }; ```
Author
Owner

@Kinfe123 commented on GitHub (Apr 8, 2025):

you dont have to configure it since better auth uses better-auth.session_token by default so you dont need to prefix it actually unless you have a custom prefix .. you can just pass the request object only

<!-- gh-comment-id:2785767737 --> @Kinfe123 commented on GitHub (Apr 8, 2025): you dont have to configure it since better auth uses `better-auth.session_token` by default so you dont need to prefix it actually unless you have a custom prefix .. you can just pass the request object only
Author
Owner

@Kinfe123 commented on GitHub (Apr 8, 2025):

I solved it recently like this

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

export function middleware(request: NextRequest) {
const cookie = request.cookies.get("better-auth.session_token");

if (!cookie || !cookie.value) {
return NextResponse.redirect(new URL("/unauthorized", request.url));
}

return NextResponse.next();
}

export const config = {
matcher: ["/dashboard/:path*"],
};

this is what better auth does internally if you have a valid cookie stored , i dont think this is not the case

<!-- gh-comment-id:2785784356 --> @Kinfe123 commented on GitHub (Apr 8, 2025): > I solved it recently like this > > import { NextRequest, NextResponse } from "next/server"; > > export function middleware(request: NextRequest) { > const cookie = request.cookies.get("better-auth.session_token"); > > if (!cookie || !cookie.value) { > return NextResponse.redirect(new URL("/unauthorized", request.url)); > } > > return NextResponse.next(); > } > > export const config = { > matcher: ["/dashboard/:path*"], > }; this is what better auth does internally if you have a valid cookie stored , i dont think this is not the case
Author
Owner

@camunoz2 commented on GitHub (Apr 8, 2025):

Yep you are right, getSessionCookie works correctly when only the request object is passed without additional options. Maybe clarify this in the documentation, as passing only the request object is likely the common use case...i think

<!-- gh-comment-id:2786135653 --> @camunoz2 commented on GitHub (Apr 8, 2025): Yep you are right, getSessionCookie works correctly when only the request object is passed without additional options. Maybe clarify this in the documentation, as passing only the request object is likely the common use case...i think
Author
Owner

@fl0wo commented on GitHub (Apr 10, 2025):

Same here

// does not work
const sessionCookie = getSessionCookie(request, {
        // Dev Console > Application > Cookies > better-auth.session_token
        cookieName: "session_token",
        cookiePrefix: "better-auth"
    });

// does work
    request.cookies.get('better-auth.session_token');
<!-- gh-comment-id:2792131620 --> @fl0wo commented on GitHub (Apr 10, 2025): Same here ``` // does not work const sessionCookie = getSessionCookie(request, { // Dev Console > Application > Cookies > better-auth.session_token cookieName: "session_token", cookiePrefix: "better-auth" }); // does work request.cookies.get('better-auth.session_token'); ```
Author
Owner

@Kinfe123 commented on GitHub (Apr 10, 2025):

Are you using nextjs and what version of next are you actually using ?

<!-- gh-comment-id:2792264752 --> @Kinfe123 commented on GitHub (Apr 10, 2025): Are you using nextjs and what version of next are you actually using ?
Author
Owner

@Abhishek21k commented on GitHub (May 25, 2025):

Same here

// does not work
const sessionCookie = getSessionCookie(request, {
        // Dev Console > Application > Cookies > better-auth.session_token
        cookieName: "session_token",
        cookiePrefix: "better-auth"
    });

// does work
    request.cookies.get('better-auth.session_token');

same thing happening here

<!-- gh-comment-id:2907776031 --> @Abhishek21k commented on GitHub (May 25, 2025): > Same here > > ``` > // does not work > const sessionCookie = getSessionCookie(request, { > // Dev Console > Application > Cookies > better-auth.session_token > cookieName: "session_token", > cookiePrefix: "better-auth" > }); > > // does work > request.cookies.get('better-auth.session_token'); > ``` same thing happening here
Author
Owner

@Aztaro97 commented on GitHub (Jun 26, 2025):

I encountered the same issue where getSessionCookie returns null in Next.js 15.2 Edge Runtime. The problem is that Better Auth's session cookie handling isn't fully compatible with the Edge Runtime environment.

To Fix it: Add runtime: "nodejs" to your middleware configuration:

import { NextRequest, NextResponse } from "next/server";
import { getSessionCookie } from "better-auth/cookies";
 
export function middleware(request: NextRequest) {
  const session = getSessionCookie(request);
 
  if (!session) {
    return NextResponse.redirect(new URL("/unauthorized", request.url));
  }
 
  return NextResponse.next();
}
 
export const config = {
  matcher: ["/dashboard/:path*"],
  runtime: "nodejs"  // This fixes the issue
};
<!-- gh-comment-id:3007504085 --> @Aztaro97 commented on GitHub (Jun 26, 2025): I encountered the same issue where `getSessionCookie` returns `null` in Next.js 15.2 Edge Runtime. The problem is that Better Auth's session cookie handling isn't fully compatible with the Edge Runtime environment. To **Fix it:** Add `runtime: "nodejs"` to your middleware configuration: ```javascript import { NextRequest, NextResponse } from "next/server"; import { getSessionCookie } from "better-auth/cookies"; export function middleware(request: NextRequest) { const session = getSessionCookie(request); if (!session) { return NextResponse.redirect(new URL("/unauthorized", request.url)); } return NextResponse.next(); } export const config = { matcher: ["/dashboard/:path*"], runtime: "nodejs" // This fixes the issue }; ```
Author
Owner

@mmolina-wortise commented on GitHub (Jul 28, 2025):

Hi, i'm trying to replicate your solution but when I implement runtime: "nodejs" I got this error:

Error: Cannot find the middleware module

Please help me to solve it.
Versions list:
next: '15.4.4'
better-auth: '1.2.7'

This is my code:

import { getSessionCookie }          from 'better-auth/cookies'
import { NextRequest, NextResponse } from 'next/server'
import createIntlMiddleware from 'next-intl/middleware'
import { routing }          from './i18n/routing'

const intlMiddleware = createIntlMiddleware(routing)

export async function middleware(request: NextRequest) {

  const path          = request.nextUrl.pathname
  const sessionCookie =  getSessionCookie(request)

  console.log({ sessionCookie: JSON.stringify(sessionCookie, null, 2) })

  // e.g. /en-US/
  const isRootRoute = normalizePath(path) === ''

  if (isRootRoute) {

    return NextResponse.redirect(new URL('/home', request.url))
  }

  return intlMiddleware(request)
}

export const config = {
  matcher: ['/((?!api|trpc|_next|_vercel|.*\\..*).*)'],
  runtime: 'nodejs'
}

<!-- gh-comment-id:3128500246 --> @mmolina-wortise commented on GitHub (Jul 28, 2025): Hi, i'm trying to replicate your solution but when I implement `runtime: "nodejs"` I got this error: `Error: Cannot find the middleware module` Please help me to solve it. Versions list: `next: '15.4.4'` `better-auth: '1.2.7'` This is my code: ``` import { getSessionCookie } from 'better-auth/cookies' import { NextRequest, NextResponse } from 'next/server' import createIntlMiddleware from 'next-intl/middleware' import { routing } from './i18n/routing' const intlMiddleware = createIntlMiddleware(routing) export async function middleware(request: NextRequest) { const path = request.nextUrl.pathname const sessionCookie = getSessionCookie(request) console.log({ sessionCookie: JSON.stringify(sessionCookie, null, 2) }) // e.g. /en-US/ const isRootRoute = normalizePath(path) === '' if (isRootRoute) { return NextResponse.redirect(new URL('/home', request.url)) } return intlMiddleware(request) } export const config = { matcher: ['/((?!api|trpc|_next|_vercel|.*\\..*).*)'], runtime: 'nodejs' } ```
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: github-starred/better-auth#9079