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
相关产品推荐
相关产品推荐

