iPhone Safari需关闭跨站追踪才能使用电商APP心愿单功能问题
问题场景
开发的React+Node.js电商APP,前端部署在Vercel,后端部署在Render,采用HTTP-only Cookie实现身份认证,包含心愿单添加与查看功能。桌面端所有主流浏览器(Chrome、Edge、Firefox、Safari)功能正常,但在iPhone Safari中,必须手动关闭「阻止跨站追踪」设置,心愿单功能才能正常使用。
具体问题:iPhone Safari可正常完成登录,但调用POST /user/wishlist添加商品至心愿单或GET /user/wishlist查看心愿单时,请求静默失败或未携带认证Cookie,仅关闭跨站追踪设置后恢复正常。
相关代码
后端代码
app.use( cors({ origin: [ 'https://e-shop-drab.vercel.app', // Vercel生产域名 'http://localhost:5173', // 开发环境域名 'https://eshop-vqu6.onrender.com' // 测试用Ngrok域名 ], methods: ['GET', 'POST', 'OPTIONS'], credentials: true, allowedHeaders: ['Content-Type', 'Authorization'] }) ); const cookieParser = require('cookie-parser'); app.use(cookieParser()); const jwt = require('jsonwebtoken'); const generateTokenAndSetCookie = (userId, res) => { const token = jwt.sign({ userId }, process.env.JWT_SECRET, { expiresIn: '1h', }); res.cookie('token', token, { httpOnly: true, secure: true, sameSite: 'None', maxAge: 3600 * 1000, }); return token; }; module.exports = generateTokenAndSetCookie;
前端代码
import axios from 'axios'; const api = axios.create({ baseURL: "https://eshop-vqu6.onrender.com", withCredentials: true, headers: { 'Content-Type': 'application/json' } }) const CheckLogin = () => api.get("/user/check-login"); const PostSignup = (signup) => api.post("/user/signup",signup); const PostLogin = (login) => api.post("/user/login",login); const Logout = () => api.post("/user/logout",{}, { withCredentials: true }); const addToWishList = (wishlist) => api.post("/user/wishlist",wishlist,{ withCredentials: true }); const getWishlist = () => api.get("/user/wishlist",{ withCredentials: true }); const removeWishlist = (productID) => api.post("/user/removewishlist",{ productID }); export {CheckLogin, PostSignup, PostLogin,Logout,addToWishList,getWishlist,removeWishlist};
问题原因
iPhone Safari的「阻止跨站追踪」(ITP,Intelligent Tracking Prevention)机制会严格限制跨域场景下的第三方Cookie。当前前端(Vercel域名)与后端(Render域名)属于跨域,即便Cookie设置了sameSite: None和secure: true,ITP仍会将其标记为跨站追踪Cookie,在后续请求中自动拦截不携带,导致身份认证失败,心愿单接口无法正常调用。
解决方案
方案1:为Cookie添加Partitioned属性
Safari 15.4及以上版本支持Partitioned属性,配合sameSite: None和secure: true使用,可让跨域Cookie绕过ITP限制,在请求中正常携带。修改后端generateTokenAndSetCookie函数中的Cookie配置:
res.cookie('token', token, { httpOnly: true, secure: true, sameSite: 'None', maxAge: 3600 * 1000, Partitioned: true, // 新增该属性 });
方案2:优化CORS配置
- 确保CORS的
origin数组包含前端实际请求的完整域名,避免使用通配符; - 补充接口用到的HTTP方法,确保预检请求(OPTIONS)正常通过;
- 移除未实际使用的请求头配置,减少冗余:
app.use( cors({ origin: [ 'https://e-shop-drab.vercel.app', 'http://localhost:5173', ], methods: ['GET', 'POST', 'OPTIONS'], credentials: true, allowedHeaders: ['Content-Type'], // 若未使用Authorization可移除 optionsSuccessStatus: 200, // 兼容旧版本浏览器 }) );
方案3:统一前端axios配置
axios实例已设置withCredentials: true,后续请求无需重复配置,可简化代码避免遗漏:
const api = axios.create({ baseURL: "https://eshop-vqu6.onrender.com", withCredentials: true, headers: { 'Content-Type': 'application/json' } }) // 简化请求方法,无需重复设置withCredentials const addToWishList = (wishlist) => api.post("/user/wishlist", wishlist); const getWishlist = () => api.get("/user/wishlist");
方案4:同站部署(根源解决)
若条件允许,将前端和后端部署在同一主域名下(如前端shop.yourdomain.com,后端api.shop.yourdomain.com),此时属于同站请求,Safari的ITP不会拦截Cookie,彻底解决跨域Cookie问题。
验证步骤
- 修改代码后重新部署后端和前端;
- 在iPhone Safari中开启「阻止跨站追踪」设置;
- 登录后尝试添加/查看心愿单,检查请求是否携带
tokenCookie,确认接口正常响应。
内容的提问来源于stack exchange,提问作者Anan328

