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路由里),有时候是客户端跳转,所以一定要保证两种环境下都能正常工作。




