登录回调跳到错误的 URL:3 个原因 + 修复路径

登录回调跳回 localhost 或错误域——allowed redirect URI 配置问题。

你在生产环境点 Google / GitHub 登录,授权完成后浏览器跳回的是 http://localhost:3000/auth/callback(你看到的是空白页),或者跳到一个完全不相关的域,又或者 OAuth provider 直接报 “redirect_uri_mismatch”。这是 OAuth 流程里最常见的配置坑:你代码里发给 provider 的 redirect_uri 跟 provider 后台允许列表里的那串字符不完全相等。

OAuth provider(Google、Auth0、Supabase、Clerk)的 redirect_uri 校验是逐字符精确匹配:协议(http vs https)、域名、端口、路径、尾部斜杠,任何一处不一致都会被拒。理解这点,问题排查就有了准星。

常见原因

按命中率从高到低:

1. 生产 callback URL 不在允许列表

最高频。你在 dev 测试时加了 http://localhost:3000/auth/callback,部署上生产后忘了把 https://yourdomain.com/auth/callback 也加进去。

如何判断:provider 错误页通常显示”redirect_uri_mismatch”和它收到的 URI;对照 provider 后台的 allowed list 看是否包含。

2. 代码里硬编码了 localhost

const redirectUri = "http://localhost:3000/auth/callback"; // ❌ 永远 dev URL

部署后这行没变,请求里 redirect_uri= 还是 localhost,provider 拒收。

如何判断:搜 codebase 是否硬编码 localhost 或固定域名。

3. 协议或尾斜杠不匹配

https://example.com/cbhttps://example.com/cb/(多了一个斜杠)被认为不同。http:// vs https:// 也是。

如何判断:浏览器 Network 面板看请求里的 redirect_uri=,跟 provider 后台逐字符对。

4. 用了 www. vs 裸域

https://www.example.com/cbhttps://example.com/cb 不同。如果你站点同时支持两个但只在允许列表里加了一个,从另一个发起就失败。

如何判断:你的站点是否有 www → 非 www 重定向(或反过来)?

5. preview / staging 环境的 URL 没加

Vercel preview deployment 每次都是 xxx-yyy.vercel.app 这种随机域,OAuth 在 preview 环境必然失败,除非你专门处理。

如何判断:是否在 preview deployment 触发?

6. Provider 自动 strip 路径或 query

某些 provider(特别是 SAML / 老旧 SSO)会忽略 path,只匹配 origin。你在 redirect_uri 加了 ?from=signup query,被剥掉后跳到 root。

如何判断:跳回的 URL 是 / 而不是你写的 /auth/callback

最短修复路径

Step 1:抓 provider 收到的实际 redirect_uri

点登录按钮,浏览器 Network 面板看发给 provider 的请求:

https://accounts.google.com/o/oauth2/v2/auth?
  client_id=...&
  redirect_uri=https%3A%2F%2Fyourdomain.com%2Fauth%2Fcallback&
  ...

redirect_uri= 的值复制下来(URL-decode 一下),这就是 provider 收到的字符串。

Step 2:在 provider 后台把它加入白名单

把上一步复制的字符串原样贴到 provider 的 Allowed Redirect URIs:

Google Cloud Console:
  APIs & Services → Credentials → OAuth client → Authorized redirect URIs

Auth0:
  Application → Allowed Callback URLs

Supabase:
  Authentication → URL Configuration → Site URL + Redirect URLs

Clerk:
  Paths → Settings

加完保存。Google 的设置生效需要 5-10 分钟(其他 provider 即时)。

Step 3:代码里改用 env 驱动 base URL

// Next.js 示例
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL; // 永远不要 fallback 到 localhost
if (!baseUrl) throw new Error("NEXT_PUBLIC_SITE_URL not set");

const redirectUri = `${baseUrl}/auth/callback`;

然后在 host 平台(Vercel / Netlify / Railway)按环境配置:

Local (.env.local):    NEXT_PUBLIC_SITE_URL=http://localhost:3000
Preview:               NEXT_PUBLIC_SITE_URL=(动态,见 Step 4)
Production:            NEXT_PUBLIC_SITE_URL=https://yourdomain.com

Step 4:处理 preview deployment

Vercel 每个 preview 是不同子域。两个方案:

方案 A(简单但宽松):
  在 provider 加 https://*.vercel.app 通配(如果 provider 支持)

方案 B(推荐,安全):
  preview 用 process.env.VERCEL_URL 拼:
  const baseUrl = process.env.VERCEL_URL
    ? `https://${process.env.VERCEL_URL}`
    : process.env.NEXT_PUBLIC_SITE_URL;
  然后在 provider 加固定的 https://your-app-git-*-team.vercel.app
  或者只用一个固定的 preview alias(推荐)

Step 5:端到端验证

无痕窗口跑:

1. 访问生产 URL
2. 点登录
3. Network 面板看请求里 redirect_uri 是不是对的
4. 走完 Google 授权
5. 跳回页面,看 cookie / session 是否落上
6. 刷新一次,看登录态是否保持

任何一步失败,看 console / network 错误信息。

Step 6:协议 / 尾斜杠精确对齐

如果还失败,把 provider 后台和代码里的 redirect_uri 各打印一行,diff 一下:

echo "provider: https://example.com/auth/callback"  # 后台里的
echo "code:     https://example.com/auth/callback/" # 代码里的(多个 /)
diff <(echo ...) <(echo ...)

差一个字符都不行。

预防建议

  • 允许列表跟 deploy workflow 一起维护:新增环境时加 redirect URI 是 deploy checklist 一项
  • 代码里禁止硬编码 localhost / 固定域名,CI 加 lint 规则扫
  • env 命名一致:dev / preview / production 都用 NEXT_PUBLIC_SITE_URL,行为统一
  • preview 环境提前规划:要么白名单通配,要么固定 alias
  • 上线前在生产域跑一次完整登录流程,不要只本地测
  • provider 后台允许列表加注释(哪个环境用、谁加的、什么时候),方便后续审计
  • 定期 review 允许列表,删除已下线的 dev / staging URL(残留的旧 URL 是安全隐患)

相关阅读

标签: #后端 #排查 #排查