Heroku部署的Express应用中passport-google-oauth20登录后触发应用错误的问题排查
核心原因
Heroku 作为反向代理平台,所有外部请求都会先经过 Heroku 的负载均衡器再转发到你的 Express 应用。默认情况下,Express 会认为这些转发过来的请求是 HTTP 协议,而不是用户实际发起的 HTTPS 请求。这就导致 Passport 生成的 Google OAuth 回调 URL 使用了 http:// 协议,但你在 Google 开发者控制台配置的授权重定向 URI 是 https://,两者不匹配触发了 Google 的安全校验错误,最终显示 Heroku 的「Application error」页面。不过此时 OAuth 流程其实已经完成(用户信息已存入 MongoDB),所以直接访问主页会处于登录状态。
解决方案
1. 让 Express 信任 Heroku 的反向代理
在你的根目录 index.js 中,初始化 Express 应用后立即添加以下代码,让 Express 从 Heroku 提供的 X-Forwarded-Proto 请求头中获取真实的请求协议:
const app = express() // 添加这一行,让Express信任Heroku的反向代理 app.set('trust proxy', true); // 后续的中间件配置...
这一步是关键,它会让 req.protocol 正确返回用户实际使用的 https,从而让 Passport 生成符合 Google 配置的 HTTPS 回调 URL。
2. 确认 Google 开发者控制台的重定向 URI 完全匹配
检查你在 Google Cloud Console 中配置的「授权重定向 URI」是否和 Heroku 应用的回调路径完全一致,格式必须为:
https://<你的Heroku应用域名>/auth/google/callback
比如你的应用域名是 my-app.herokuapp.com,那么 URI 就是 https://my-app.herokuapp.com/auth/google/callback,必须包含完整的 https 协议和正确的路径。
3. (可选)修正 User 模型字段匹配问题
看你的代码里,Passport 查找用户用的是 User.findOne({ googleId: profile.id }),但创建新用户时的字段是 Id: profile.id,这里可能存在字段不匹配的问题(如果你的 User 模型里存储 Google ID 的字段是 googleId 的话)。建议把 newUser 里的字段改为:
const newUser = { googleId: profile.id, // 改为googleId,和查找条件一致 provider: profile.provider, displayName: profile.displayName, firstName: profile.name.givenName, lastName: profile.name.familyName, image: profile.photos[0].value, email: profile.emails[0].value, }
否则可能会导致每次登录都创建新用户,而不是匹配已存在的用户。
验证步骤
- 将修改后的代码重新部署到 Heroku
- 清除浏览器缓存或使用无痕模式测试登录流程
- 查看 Heroku 日志(你写的
log中间件会打印request.protocol),确认请求协议已经变为https
这样就能解决回调时的协议不匹配问题,登录后会正常跳转到主页,而不会停留在错误页面。
内容的提问来源于stack exchange,提问作者Dee

