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

Next.js部署Vercel时调用API出现401未授权错误求助

Next.js部署Vercel后API接口返回401未授权问题排查

问题背景

本地运行Next.js应用时一切正常,但部署到Vercel后,调用/api/posts接口始终返回401未授权错误。

相关代码文件

app/api/posts/route.js

// app/api/posts/route.js

import { NextResponse } from 'next/server';

export async function GET() {
    return NextResponse.json({ items: [{id:1, title:"Hello World"}]})
}

export async function POST() {
    return NextResponse.json({ hello: "abc"})
}

app/blog/page.js

// app/blog/page.js

import getData from "@/app/lib/getData"

export default async function BlogPage() {
  const data = await getData()
  const items = data && data.items ? [...data.items] : []

  console.log(data)
  console.log(process.env.NEXT_PUBLIC_VERCEL_ENV)
  console.log(process.env.NEXT_PUBLIC_VERCEL_URL)

  return (
    <main>
      <h1>Blog</h1>

      <p>posts:</p>
      {items && items.map((item, idx)=>{
        return <li key={`post-${idx}`}>{item.title}</li>  
      })}
    </main>
  )
}

app/lib/getDomain.js

// app/lib/getDomain.js

export default function getDomain() {
    console.log(process.env.NEXT_PUBLIC_VERCEL_ENV)
    console.log(process.env.NEXT_PUBLIC_VERCEL_URL)
    const protocol = process.env.NEXT_PUBLIC_VERCEL_ENV === "production" ? "https" : "http" 
    const domain = process.env.NEXT_PUBLIC_VERCEL_URL ? process.env.NEXT_PUBLIC_VERCEL_URL : "localhost:3000"

    return `${protocol}://${domain}`
}

app/lib/getData.js

// app/lib/getData.js

import getDomain from "@/app/lib/getDomain"

export default async function getData() {
  const domain = getDomain()
  const endpoint = `${domain}/api/posts`

  console.log(endpoint)

  const res = await fetch(endpoint)

  console.log(res)

  if (!res.ok) {
    throw new Error('Failed to fetch data')
  }

  if (res.headers.get('content-type') !== 'application/json') {
    return {items:[]}
  }

  return res.json()
}

控制台日志

production
my-vercel-123abc-projects.vercel.app
https://my-vercel-123abc-projects.vercel.app/api/posts
production
my-vercel-123abc-projects.vercel.app
https://my-vercel-123abc-projects.vercel.app/api/posts
Response {
  [Symbol(realm)]: null,
  [Symbol(state)]: {
    aborted: false,
    rangeRequested: false,
    timingAllowPassed: true,
    requestIncludesCredentials: true,
    type: 'default',
    status: 401,
    timingInfo: {
      startTime: 1758.9642749999948,
      redirectStartTime: 0,
      redirectEndTime: 0,
      postRedirectStartTime: 1758.9642749999948,
      finalServiceWorkerStartTime: 0,
      finalNetworkResponseStartTime: 0,
      finalNetworkRequestStartTime: 0,
      endTime: 0,
      encodedBodySize: 0,
      decodedBodySize: 0,
      finalConnectionTimingInfo: null
    },
    cacheState: '',
    statusText: 'Unauthorized',
    headersList: HeadersList {
      cookies: [Array],
      [Symbol(headers map)]: [Map],
      [Symbol(headers map sorted)]: null
    },
    urlList: [ [URL] ],
    body: { stream: undefined, length: undefined, source: undefined }
  },
  [Symbol(headers)]: HeadersList {
    cookies: [
      '_vercel_sso_nonce=KDscsdfgregvgVderlP3YmHxS; Max-Age=3600; Path=/; Secure; HttpOnly; SameSite=Lax'
    ],
    [Symbol(headers map)]: Map(11) {
      'cache-control' => [Object],
      'connection' => [Object],
      'content-length' => [Object],
      'content-type' => [Object],
      'date' => [Object],
      'server' => [Object],
      'set-cookie' => [Object],
      'strict-transport-security' => [Object],
      'x-frame-options' => [Object],
      'x-robots-tag' => [Object],
      'x-vercel-id' => [Object]
    },
    [Symbol(headers map sorted)]: null
  }
}

问题分析与解决方案

核心原因

你在getData.js中通过完整域名的外部HTTP请求调用同应用内的API接口,在Vercel环境下,这种请求会触发平台的SSO验证机制,导致返回401错误。而本地开发环境没有该验证逻辑,所以运行正常。

另外,Next.js App Router的服务器组件可以直接调用业务逻辑,不需要通过HTTP请求,这才是更高效的正确做法。

修复步骤

  1. 抽离公共业务逻辑
    创建app/lib/posts.js,把API路由中的业务逻辑抽成独立函数:

    // app/lib/posts.js
    export async function getPosts() {
      return { items: [{id:1, title:"Hello World"}] };
    }
    
    export async function createPost() {
      return { hello: "abc" };
    }
    
  2. 更新API路由
    修改app/api/posts/route.js,调用抽离后的公共函数:

    // app/api/posts/route.js
    import { NextResponse } from 'next/server';
    import { getPosts, createPost } from '@/app/lib/posts';
    
    export async function GET() {
      const data = await getPosts();
      return NextResponse.json(data);
    }
    
    export async function POST() {
      const data = await createPost();
      return NextResponse.json(data);
    }
    
  3. 修改页面组件
    更新app/blog/page.js,直接调用公共函数获取数据,不再通过fetch请求API:

    // app/blog/page.js
    import { getPosts } from "@/app/lib/posts"
    
    export default async function BlogPage() {
      const data = await getPosts()
      const items = data?.items || []
    
      return (
        <main>
          <h1>Blog</h1>
    
          <p>posts:</p>
          {items.map((item, idx)=>(
            <li key={`post-${idx}`}>{item.title}</li>  
          ))}
        </main>
      )
    }
    
  4. 清理冗余文件
    可以删除app/lib/getDomain.js和app/lib/getData.js,因为已经不需要通过外部请求获取数据。

额外说明

如果确实需要在客户端组件中调用API(比如用户交互场景),直接使用相对路径/api/posts即可,Vercel会正确处理内部路由,不会触发SSO验证:

// 客户端组件中使用
const res = await fetch('/api/posts');

内容的提问来源于stack exchange,提问作者Justin Bates

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.04 17:29:53