Next.js 如何在 Vercel 生产、预览和本地环境中获取正确的 base URL?
可以继续按环境配置 URL,并在服务端统一生成 OAuth 回调地址:生产环境显式配置自定义域名,预览环境使用指定的预览域名或 Vercel 部署地址,本地开发使用 localhost。
如果只有服务端需要这个值,可以将变量命名为 APP_URL;浏览器也需要读取固定配置时,才需要 NEXT_PUBLIC_APP_URL。
先确定预览环境需要哪一种地址
VERCEL_URL 是当前部署自动生成的域名,不包含 https://,也不保证等于你希望使用的 https://feature-branch.vercel.app。
配置方式取决于回调要返回的地址:
- 如果回调应返回当前这次 Preview 部署,可以使用
https://${process.env.VERCEL_URL}。 - 如果 pre-prod 分支始终使用
https://feature-branch.vercel.app,就为该分支显式配置这个地址,并确保域名已指向对应部署。
Vercel 也提供 VERCEL_BRANCH_URL,但它是平台生成的分支域名,不能据此推断你手动指定的域名。Vercel 系统环境变量文档
按环境配置 APP_URL
可以采用以下配置:
| 环境 | APP_URL | 处理方式 |
|---|---|---|
| Production | https://example.com | 始终使用生产自定义域名 |
| Preview:固定 pre-prod 地址 | https://feature-branch.vercel.app | 为对应分支设置覆盖值 |
| Preview:每次部署独立地址 | 不设置 | 自动使用 VERCEL_URL |
| 本地开发 | http://localhost:3000 | 写入项目根目录的 .env.local |
本地配置示例:
APP_URL=http://localhost:3000
在 Vercel 控制台中,将生产地址限定在 Production 环境。如果使用固定的 pre-prod 地址,将其限定在对应的 Preview 分支,避免所有预览分支都跳转到同一地址。Vercel 支持环境和分支级别的变量配置;修改后需要创建新的部署才会生效。Vercel 环境变量文档
在服务端统一解析 base URL
下面的工具函数适用于上述三个环境:优先使用显式配置,在 Preview 中允许自动获取部署地址,缺少生产配置时则直接报错。
// lib/base-url.ts
// 仅供服务端代码调用。
function parseOrigin(value: string): string {
const url = new URL(value);
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
throw new Error('APP_URL must use http or https');
}
if (
url.username ||
url.password ||
url.pathname !== '/' ||
url.search ||
url.hash
) {
throw new Error('APP_URL must contain only an origin');
}
return url.origin;
}
export function getBaseUrl(): string {
const configuredUrl = process.env.APP_URL?.trim();
if (configuredUrl) {
return parseOrigin(configuredUrl);
}
const environment = process.env.VERCEL_ENV;
if (environment === 'production') {
throw new Error('APP_URL is required in production');
}
if (environment === 'preview') {
const deploymentHost = process.env.VERCEL_URL?.trim();
if (!deploymentHost) {
throw new Error('VERCEL_URL is missing in preview');
}
return parseOrigin(`https://${deploymentHost}`);
}
if (
environment === 'development' ||
process.env.NODE_ENV === 'development'
) {
return 'http://localhost:3000';
}
throw new Error('Set APP_URL for this environment');
}
这里约定 base URL 只包含协议、主机和可选端口,不包含路径。如果本地使用其他端口,设置对应的 APP_URL 即可。
VERCEL_ENV 可以区分 production、preview 和 development。请确认项目开启了系统环境变量暴露,否则不能依赖这些变量。Vercel 系统环境变量文档
不要只用 NODE_ENV === 'production' 判断是否运行在正式生产站点,因为 Preview 也可能运行生产构建。NODE_ENV 用来判断开发模式,Vercel 部署环境则由 VERCEL_ENV 区分。
生成完整回调地址时使用 new URL():
import { getBaseUrl } from './base-url';
// 示例路径,请替换为应用实际的 OAuth 回调路由。
const redirectUri = new URL(
'/api/auth/callback',
getBaseUrl()
).toString();
这样可以避免手动拼接字符串时出现多余或缺失的 /,并按上述配置生成各环境对应域名下的回调地址。
客户端需要地址时如何处理
如果浏览器只需要当前页面所属站点的地址,可以在事件处理函数或其他确定运行于浏览器的代码中读取:
function getCurrentOrigin(): string {
return window.location.origin;
}
这个地址会跟随用户实际访问的域名,但不能用于服务端执行,也不适合要求固定生产域名的场景。
如果客户端需要预先指定的固定地址,也可以继续使用原来的 NEXT_PUBLIC_APP_URL 方案:
const appUrl = process.env.NEXT_PUBLIC_APP_URL;
NEXT_PUBLIC_* 会在 next build 时写入客户端 JavaScript,构建后不会随着运行环境自动变化。同一构建产物迁移到另一个环境时,该值仍然是原来的构建值;需要运行时配置时,应由服务端通过接口或页面数据提供。Next.js 环境变量文档
OAuth 回调还需要检查什么
除了生成正确的 URL,还需要检查 OAuth 提供商和认证库的配置:
- 如果提供商要求预先登记回调地址,生产、本地和预览地址都必须符合其登记规则。
- 每次部署都会变化的 Preview 地址可能不适合回调白名单;这种情况下,可以考虑使用已经绑定好的固定 pre-prod 域名。
- 如果认证库有自己的外部 URL 配置,需要让它与这里的地址保持一致。仅生成一个字符串,不会自动改变认证库实际发送的
redirect_uri。 - 不要直接把未经校验的请求参数或客户端提交的地址当作 OAuth 回调域名。
备注:内容仅供参考。
