You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ngrok转发本地端口后无法获取Cookie和Session问题求助

问题场景

执行ngrok http --host-header=rewrite 5000转发本地端口后,出现Cookie和Session失效问题:本地环境中Cookie、Session功能正常,但通过Ngrok访问时,React应用用户的浏览器看不到Cookie,登出接口返回401未授权。

现有配置代码

CORS配置

app.use(cookieParser());
app.use(cors({
  origin: true,
  methods: ['GET', 'POST', 'PUT', 'DELETE'],
  allowedHeaders: ['Content-Type', 'Authorization', 'Accept', 'Origin', 'X-Requested-With', 'X-Access-Token', 'X-Key', 'Cookies', 'Cache-Control', 'Set-Cookie', ],
  credentials: true
}));

登录控制器代码

export const login = async (req: Request, res: Response) => {
  const { email, password } = req.body;
  
  const schema = Joi.object({
    email: Joi.string().email().required(),
    password: Joi.string().min(3).required(),
  });
  const { error } = schema.validate(req.body);
  if (error) return res.status(400).json(errorRespone(error.details[0].message));

  const user = await Manager.findOneBy(User,{email : email});

  if(!user) return res.status(404).json(errorRespone(`User with email ${email} not found`));
  if(user.is_verified === false ) return res.status(404).json(errorRespone(`User with email ${email} not verified`));

  const checkPassword = await bcrypt.compare(password, user.password)
  if(!checkPassword) return res.status(400).json({msg : "Password not match"})

  const payload = {user_id : user._id, email : user.email, nama: user.nama, role: user.role};
  const accessToken = accessTokenSign(payload, "30m");
  const refreshToken = refreshTokenSign(payload,"1d");

  try {
    await Manager.update(User, {_id : user._id}, {refresh_token : refreshToken})

    res.cookie("refresh_token", refreshToken, {
       httpOnly : true,
       maxAge : 24 * 60 * 60 * 1000,
       sameSite : "none",
       secure : true
    })

    const loginUser = {
      email : user.email,
      nama : user.nama,
      role : user.role,
      is_verified : user.is_verified
    }

    req.session["user_id"] = user._id


    res.status(200).json(respone("Success login", { accessToken, user : loginUser }));
   
  } catch (error) {
    if(error) return res.status(500).json(errorRespone(error.message))
  }
}

登出控制器代码

export const logout = async(req: Request, res: Response) => {
  const user_id = req.session['user_id'];
  console.log("session logout : "+user_id);
  
  if(!user_id) return res.status(401).json(errorRespone("Login first to access this feature"));

  try {
    await Manager.update(User, {_id : new ObjectId(user_id)}, {refresh_token : ""})
    res.clearCookie("refresh_token");
    res.status(200).json(respone("Success logout",{}));
  } catch (error) {
    if(error) return res.status(500).json(errorRespone(error.message))
  }

}

排查与解决方案

1. 修复Session的Cookie配置

你仅给refresh_token设置了跨域兼容的Cookie属性,但Session依赖的默认Cookie(通常名为connect.sid)未配置对应属性,导致在Ngrok的HTTPS跨域环境下被浏览器拦截。需要在Session中间件中添加以下配置:

app.use(session({
  secret: '你的会话密钥', // 替换为实际密钥
  resave: false,
  saveUninitialized: false,
  cookie: {
    httpOnly: true,
    maxAge: 24 * 60 * 60 * 1000,
    sameSite: "none", // 跨域场景必须设为none
    secure: true, // Ngrok是HTTPS环境,必须开启
  }
}));

2. 确保React请求携带Credentials

前端发起请求时,必须明确携带凭证,否则浏览器不会发送Cookie:

  • Axios示例:
    axios.post('/api/login', loginData, { withCredentials: true })
    axios.post('/api/logout', {}, { withCredentials: true })
    
  • Fetch示例:
    fetch('/api/login', {
      method: 'POST',
      credentials: 'include',
      body: JSON.stringify(loginData),
      headers: { 'Content-Type': 'application/json' }
    })
    

3. 优化CORS配置

  • 移除allowedHeaders中的Set-Cookie:这是响应头,不属于请求头范畴,无需添加到允许列表。
  • 若origin: true出现兼容问题,可临时替换为Ngrok的具体域名(如https://xxxx-xx-xx-xx-xx.ngrok-free.app)测试,确认后再改回true。

4. 验证Cookie是否生效

登录成功后,打开浏览器开发者工具的Application -> Cookies,查看Ngrok域名下是否存在refresh_token和connect.sid两个Cookie:

  • 若不存在,检查服务器端Cookie配置是否正确;
  • 若存在但登出仍失败,确认请求是否携带了这些Cookie。

内容的提问来源于stack exchange,提问作者zulnurdiana

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 01:36:12