使用Next-Auth Server Session时服务器响应缓慢问题求助
我在搭建Auth.js(原NextAuth)时,无论用稳定版还是测试版都遇到同一个问题:在根layout.js中通过Session判断登录状态并渲染导航栏时,部署在Vercel的前端服务器初始响应时间大幅增加,平均1.2秒,最高可达2秒;移除导航栏的Session判断逻辑后,站点加载仅需100-300ms。
我试过自定义登录页、Auth.js自带登录页,甚至启用Edge选项,都没有改善。目前已经把首页分离出来避免影响新用户,我是参照官方示例用Server Session渲染导航栏的。
唯一的明显差异是我采用JWT令牌搭配自有后端(无服务器端存储),请问为什么服务器响应时间会这么差?
相关代码
layout.js
export default async function RootLayout({ children }) { return ( <html lang="en"> <body> <ThemeRegistry> <Navbar /> {children} </ThemeRegistry> </body> </html> ) }
navbar.js
import NavLoggedIn from './NavLoggedIn'; import { auth } from "@/auth" export default async function Navbar() { const session = await auth() if (!session?.user) return <></> return ( <NavLoggedIn /> ); }
auth.js
import NextAuth from "next-auth"; import axios from "axios"; import GoogleProvider from "next-auth/providers/google"; const BACKEND_ACCESS_TOKEN_LIFETIME = 45 * 60; const BACKEND_REFRESH_TOKEN_LIFETIME = 6 * 24 * 60 * 60; const getCurrentEpochTime = () => { return Math.floor(new Date().getTime() / 1000); }; const SIGN_IN_HANDLERS = { "google": async (user, account, profile, email, credentials) => { try { const response = await axios({ method: "post", url: process.env.NEXTAUTH_BACKEND_URL + "dj-rest-auth/google/", data: { access_token: account["id_token"] }, }); account["meta"] = response.data; return true; } catch (error) { console.error(error); return false; } } }; const SIGN_IN_PROVIDERS = Object.keys(SIGN_IN_HANDLERS); export const config = { secret: process.env.AUTH_SECRET, session: { strategy: "jwt", maxAge: BACKEND_REFRESH_TOKEN_LIFETIME, }, providers: [ GoogleProvider({ clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, authorization: { params: { prompt: "consent", access_type: "offline", response_type: "code" } } }), ], callbacks: { async signIn({user, account, profile, email, credentials}) { if (!SIGN_IN_PROVIDERS.includes(account.provider)) return false; return SIGN_IN_HANDLERS[account.provider]( user, account, profile, email, credentials ); }, async jwt({user, token, account}) { if (user && account) { let backendResponse = account.provider === "credentials" ? user : account.meta; token["user"] = backendResponse.user; token["access_token"] = backendResponse.access; token["refresh_token"] = backendResponse.refresh; token["ref"] = getCurrentEpochTime() + BACKEND_ACCESS_TOKEN_LIFETIME; return token; } if (getCurrentEpochTime() > token["ref"]) { const response = await axios({ method: "post", url: process.env.NEXTAUTH_BACKEND_URL + "/token/refresh/", data: { refresh: token["refresh_token"], }, }); token["access_token"] = response.data.access; token["refresh_token"] = response.data.refresh; token["ref"] = getCurrentEpochTime() + BACKEND_ACCESS_TOKEN_LIFETIME; } return token; }, async session({token}) { return token; }, } }; export const { handlers, auth, signIn, signOut } = NextAuth(config)
核心原因分析
你的问题根源出在JWT回调中的同步后端请求阻塞了Server Component的渲染流程:
每次请求都可能触发后端令牌刷新:在
jwt回调中,只要当前时间超过token.ref,就会向自有后端发起POST /token/refresh/请求。这个请求是同步的,会阻塞Auth.js的Session解析过程,而你的导航栏作为Server Component,会在每个请求中等待这个过程完成才能渲染。Server Component的渲染依赖Session解析:根Layout中嵌套的
Navbar是异步Server Component,它调用await auth()时,Auth.js会完整执行JWT校验、可能的令牌刷新流程,这个过程中的网络请求(到你的后端)是导致响应延迟的主要原因——Vercel服务器到你的后端的网络耗时加上后端处理时间,直接叠加到了页面响应时间上。Edge Runtime优化无效的原因:即使启用Edge选项,只要JWT回调中存在跨服务的网络请求,Edge节点仍然需要等待这个请求完成,无法提前返回内容,所以延迟问题无法解决。
优化方案
方案1:将导航栏渲染改为客户端侧判断
把Navbar改为Client Component,使用useSession钩子在客户端获取Session,避免在Server Component中阻塞渲染:
'use client' import NavLoggedIn from './NavLoggedIn'; import { useSession } from "next-auth/react" export default function Navbar() { const { data: session } = useSession() if (!session?.user) return <></> return ( <NavLoggedIn /> ); }
这样页面的初始HTML可以快速返回,导航栏的状态在客户端渲染时再更新,不会阻塞服务器响应。
方案2:优化JWT令牌刷新逻辑,减少后端请求
- 延长后端AccessToken的有效期,减少刷新频率(比如从45分钟延长到1-2小时)
- 在
jwt回调中添加缓存逻辑,避免同一令牌的重复刷新请求 - 考虑将令牌刷新逻辑放在客户端,而非服务器端的JWT回调中
方案3:使用增量静态再生(ISR)或静态页面(如果适用)
如果首页不需要实时的用户状态,可以将首页设为静态页面,单独处理登录状态的客户端渲染,避免Server Component的阻塞。
额外建议
- 检查Vercel服务器到你的后端的网络延迟,可以通过Vercel的日志查看请求耗时
- 给后端的令牌刷新接口添加性能监控,确认后端处理耗时是否合理
- 考虑在Vercel和你的后端之间添加CDN或边缘缓存,减少跨区域请求延迟
内容的提问来源于stack exchange,提问作者Matthias

