You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Vercel部署的Next.js应用跨生产、预览、本地环境动态解析正确基础URL的方案咨询

Vercel部署的Next.js应用跨生产、预览、本地环境动态解析正确基础URL的方案咨询

嗨,这个需求太常见了!我自己在做Next.js + Vercel项目的时候也踩过类似的坑,分享几个我亲测好用的方案,你可以根据自己的习惯选:

方案一:优化版手动环境变量配置(省心且可靠)

你之前用NEXT_PUBLIC_APP_URL的思路其实完全没问题,这也是很多团队实际在用的方案,完全不算“不规范”。不过可以优化配置流程,减少手动重复操作:

  • 本地开发:在根目录的.env.local里直接写死NEXT_PUBLIC_APP_URL=http://localhost:3000,本地启动时会自动加载
  • Vercel生产环境:在Vercel项目的环境变量面板里,给生产环境单独设置NEXT_PUBLIC_APP_URL=https://example.com
  • Vercel预览环境:不用手动给每个分支设!在Vercel的环境变量里,把这个变量设为预览环境可用,值写https://${VERCEL_URL}——Vercel会自动把预览分支的域名替换进去,比如https://feature-branch.vercel.app

这样配置一次就搞定所有环境,不用每次开新分支都改变量,非常省心。而且NEXT_PUBLIC_前缀保证了它在客户端和服务端都能访问,完美适配OAuth跳转这类需要前后端都用的场景。

方案二:自动判断的工具函数(无需手动配置变量)

如果你不想依赖手动设置环境变量,也可以写一个轻量的工具函数,根据运行环境自动推导基础URL:
首先在项目里新建一个工具文件,比如utils/getBaseUrl.js

export function getBaseUrl() {
  // 本地开发环境
  if (process.env.NODE_ENV === 'development') {
    return 'http://localhost:3000';
  }

  // Vercel生产/预览环境
  if (process.env.VERCEL) {
    // 生产环境用自定义域名
    if (process.env.VERCEL_ENV === 'production') {
      return 'https://example.com';
    }
    // 预览环境用Vercel自动生成的域名,补全https前缀
    return `https://${process.env.VERCEL_URL}`;
  }

  // 兜底(比如其他部署环境)
  return 'https://example.com';
}

然后在需要的地方直接导入调用:

import { getBaseUrl } from '../utils/getBaseUrl';

// 生成OAuth跳转地址
const authRedirectUrl = `${getBaseUrl()}/api/auth/callback`;

这个方案的好处是不用在Vercel面板里配置额外变量,完全靠内置环境变量自动判断。需要注意的是:

  • VERCEL变量是Vercel自动注入的,只要在Vercel上运行就会存在
  • VERCEL_ENV变量Vercel会自动设为production/preview/development,用来区分环境
  • 这个函数在服务端和客户端都能正常运行,因为process.env的变量在Next.js里会被静态替换(带NEXT_PUBLIC_的客户端能拿,服务端则能直接访问所有Vercel内置变量)

方案三:Next.js 13+ App Router专属方案(更简洁)

如果你用的是Next.js 13及以上的App Router,还可以直接利用框架的headers()函数在服务端组件里获取请求头,动态推导域名:

import { headers } from 'next/headers';

export function getBaseUrl() {
  const headersList = headers();
  const host = headersList.get('host');
  
  // 本地开发
  if (process.env.NODE_ENV === 'development') {
    return 'http://localhost:3000';
  }

  // 生产/预览环境从请求头拿域名,补全https
  return `https://${host}`;
}

这个方案的优势是完全不用依赖任何环境变量,直接从请求里拿真实的访问域名,适合多域名部署的场景。不过要注意,这个函数只能在服务端组件或API路由里用,客户端组件不能直接调用headers(),如果客户端需要用的话,得通过服务端组件传props或者API接口返回。

总结:哪种方案最“ idiomatic ”?

其实没有绝对的“最佳方案”,看你的团队习惯:

  • 如果追求简单可靠,选方案一,配置一次就一劳永逸,新人接手也容易理解
  • 如果追求零手动配置,选方案二,完全靠代码自动适配
  • 如果用App Router且服务端场景多,选方案三,最贴合Next.js新特性

我自己团队现在用的是方案一,因为配置简单,而且出问题的时候排查起来也直观——毕竟环境变量是明确可见的,比代码推导更易调试。

对了,补充一个小细节:不管用哪种方案,都要确保在服务端和客户端都能拿到正确的URL,因为OAuth跳转这类场景,有时候是服务端生成跳转地址(比如API路由里),有时候是客户端跳转,所以一定要保证两种环境下都能正常工作。

火山引擎 最新活动