You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用Next-Auth Server Session时服务器响应缓慢问题求助

问题:Auth.js(NextAuth)搭配自有JWT后端导致Vercel部署后初始响应时间暴涨

我在搭建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的渲染流程:

  1. 每次请求都可能触发后端令牌刷新:在jwt回调中,只要当前时间超过token.ref,就会向自有后端发起POST /token/refresh/请求。这个请求是同步的,会阻塞Auth.js的Session解析过程,而你的导航栏作为Server Component,会在每个请求中等待这个过程完成才能渲染。

  2. Server Component的渲染依赖Session解析:根Layout中嵌套的Navbar是异步Server Component,它调用await auth()时,Auth.js会完整执行JWT校验、可能的令牌刷新流程,这个过程中的网络请求(到你的后端)是导致响应延迟的主要原因——Vercel服务器到你的后端的网络耗时加上后端处理时间,直接叠加到了页面响应时间上。

  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 06:34:51