Session Cache disappearing in Next.js

Expected behavior: Cached session refreshes in cookies every 30 seconds Current behavior: Cached session disappears after 30 seconds and never gets refreshed, leaving better-auth to default back to bringing the session from the database. Better-Auth version v1.1.3 (Latest) Bug Details: I started testing out session cache in my project, set the cache to 30 seconds and tried using it. After logging in, the cached session better-auth.session_data stays in my cookies for 30 seconds and then disappears, never to return. Better-auth then defaults back to bringing the session from the database. The only way I found to get the cached session back is to update the user, but then same loop happens, wait 30 seconds, disappear without being. This is some of my auth options for reference. I'm also using the Oragnization plugin
export const auth = betterAuth({
database: drizzleAdapter(db, { provider: 'pg' }),
session: { cookieCache: { enabled: true, maxAge: 30 } },
databaseHooks: {
session: {
create: {
before: async session => {
const { data, error: _error } = await getUserOrganizationAuth(session.userId)
return { data: { ...session, activeOrganizationId: data?.id } }
},
},
},
},
})
export const auth = betterAuth({
database: drizzleAdapter(db, { provider: 'pg' }),
session: { cookieCache: { enabled: true, maxAge: 30 } },
databaseHooks: {
session: {
create: {
before: async session => {
const { data, error: _error } = await getUserOrganizationAuth(session.userId)
return { data: { ...session, activeOrganizationId: data?.id } }
},
},
},
},
})
15 Replies
bekacru
bekacru3mo ago
what framework are you using?
Kazz
KazzOP3mo ago
Nextjs 15 latest version with React 19
bekacru
bekacru3mo ago
probably in your case the issue is you're trying to call getSession in a server component where it can't update the cookie cache. do you have a middleware where you check for the session?
Kazz
KazzOP3mo ago
I do The getSession is from the client better-auth/client
export const getCurrentSessionMiddlware = async () => {
const { data } = await getSession({ fetchOptions: { headers: await headers() } })

return data ? { user: data.user, session: data.session } : null
}
middleware.ts
export default async function authMiddleware(request: NextRequest) {
const userInfo = await getCurrentSessionMiddlware()
const isAuth = !!userInfo
if (!isAuth) return NextResponse.redirect(new URL('/', request.url))
return NextResponse.next()
}
export const getCurrentSessionMiddlware = async () => {
const { data } = await getSession({ fetchOptions: { headers: await headers() } })

return data ? { user: data.user, session: data.session } : null
}
middleware.ts
export default async function authMiddleware(request: NextRequest) {
const userInfo = await getCurrentSessionMiddlware()
const isAuth = !!userInfo
if (!isAuth) return NextResponse.redirect(new URL('/', request.url))
return NextResponse.next()
}
bekacru
bekacru3mo ago
are you using nextCookies plugin?
Kazz
KazzOP3mo ago
No im using organization and magic link plugins only
bekacru
bekacru3mo ago
add next cookies plugin
import { nextCookies } from "better-auth/next-js";
import { nextCookies } from "better-auth/next-js";
Kazz
KazzOP3mo ago
I will try them out and report back Same problem after adding nextCookies, its still disappearing after the maxAge and not refreshing. (Made sure nextCookies() us at the end of the plugin array)
bekacru
bekacru3mo ago
after it disappear in your browser try to refersh your page and when it hits the middleware try to log the response header from getSession.
await authClient.getSession({
fetchOptions: {
onResponse: (ctx)=>console.log(ctx.response.headers)
}
})
await authClient.getSession({
fetchOptions: {
onResponse: (ctx)=>console.log(ctx.response.headers)
}
})
and check if the response header has cookies fyi, it's not a bug in general. it works. most likely it's a setup issue.
Kazz
KazzOP3mo ago
GET /api/auth/get-session 200 in 287ms
Headers { }
GET /api/auth/get-session 200 in 287ms
Headers { }
my function
import { getSessionMiddleware as getSession, organizationMiddleware as organization } from './auth-middelware'
export const getCurrentSessionMiddlware = async () => {
const { data } = await getSession({
fetchOptions: {
headers: await headers(),
onResponse: ctx => console.log(ctx.response.body),
},
})

return data ? { user: data.user, session: data.session } : null
}
import { getSessionMiddleware as getSession, organizationMiddleware as organization } from './auth-middelware'
export const getCurrentSessionMiddlware = async () => {
const { data } = await getSession({
fetchOptions: {
headers: await headers(),
onResponse: ctx => console.log(ctx.response.body),
},
})

return data ? { user: data.user, session: data.session } : null
}
headers returning empty object for some reason Some updates: useSession refreshes the cookie cache, its only the getSession the one that is not refreshing it I am calling the getSession in my dashboard pages as well, so its not only in the middleware. - The middleware calls from the authClient better-auth/client - While getSession in my dashboard calls from the authClient better-auth/react
Kazz
KazzOP3mo ago
This is the header response log from the dashboard getSession
bekacru
bekacru3mo ago
getSession is here trying to set cookie as well. The problem with your dashboard is cause you're calling getSession on a server component. Since they can't set cookies the cookie cache won't be set. the middleware should have set the cookie assuming cookie works for middleware but it may not im not sure.
Kazz
KazzOP2mo ago
Yes the getSession in the middleware doesnt give back headers as you can see. I'll try other approaches of calling the session in the middleware, but i don't really know what to do after that if it didn't work I'll report back after I tinker with it a bit @bekacru This is the ugly code I ended up with to fix the problem. 1. I took the headers from the session call and stored it in a ResponseContext variable. 2. Replaced return NextResponse with a response variable with type NextResponse so I dont return the response until the whole middleware finishes 3. I took the set-cookies from the headers and appended each one in the context at the end of my logic I'm pretty sure this is no way near best practices, do you have any idea of a better fix?
export async function middleware(request: NextRequest) {
let sessionResponse: ResponseContext | undefined
const { data: userInfo } = await getSessionMiddleware({
fetchOptions: {
headers: request.headers,
onResponse: ctx => {
sessionResponse = ctx
},
},
})
const isAuth = !!userInfo

if (!isAuth) return NextResponse.redirect(new URL('/', request.url))
const organization = userInfo.session.activeOrganizationId

let response: NextResponse
// My logic for protection
if (request.nextUrl.pathname.startsWith('/dashboard')) {
// Logic here for dashboard page
} else if (request.nextUrl.pathname.startsWith('/onboarding')) {
// Logic here for onboarding page
} else {
// If user is trying to access a different page, allow access
response = NextResponse.next()
}

// Set cookies from the session response
const cookies = sessionResponse?.response.headers.getSetCookie()
cookies?.forEach(cookie => response.headers.append('Set-Cookie', cookie))

return response
}

export const config = { matcher: ['/dashboard/:path*', '/onboarding/:path*'] }
export async function middleware(request: NextRequest) {
let sessionResponse: ResponseContext | undefined
const { data: userInfo } = await getSessionMiddleware({
fetchOptions: {
headers: request.headers,
onResponse: ctx => {
sessionResponse = ctx
},
},
})
const isAuth = !!userInfo

if (!isAuth) return NextResponse.redirect(new URL('/', request.url))
const organization = userInfo.session.activeOrganizationId

let response: NextResponse
// My logic for protection
if (request.nextUrl.pathname.startsWith('/dashboard')) {
// Logic here for dashboard page
} else if (request.nextUrl.pathname.startsWith('/onboarding')) {
// Logic here for onboarding page
} else {
// If user is trying to access a different page, allow access
response = NextResponse.next()
}

// Set cookies from the session response
const cookies = sessionResponse?.response.headers.getSetCookie()
cookies?.forEach(cookie => response.headers.append('Set-Cookie', cookie))

return response
}

export const config = { matcher: ['/dashboard/:path*', '/onboarding/:path*'] }
Hey @bekacru Got a chance to look at this? This is the only solution i found so far for the cookie session cache problem on nextjs middleware Hey @Ruii sorry for ping, I just wanted to ask if you had the same problem as me?
Unknown User
Unknown User2mo ago
Message Not Public
Sign In & Join Server To View

Did you find this page helpful?