从 `AliPay` 重定向后 Chrome 清除 local storage 缓存
结论
AliPay 的普通页面重定向不会主动清除网站的 localStorage。更常见的原因是,支付完成后的返回页面与支付前的页面不属于同一个 origin,因此浏览器读取的是另一份空的 localStorage,看起来就像缓存被清除了。
网络环境可能间接造成这种差异。例如,办公室的 DNS、代理、网关或负载均衡可能把返回地址改成不同的协议、域名或端口。除此之外,还要检查响应中的 Clear-Site-Data 头、Chrome 扩展、企业策略,以及业务代码是否调用了 localStorage.clear()。
先确认返回地址是否属于同一个 origin
localStorage 按以下三项隔离:
协议 + 主机名 + 端口
只要其中一项不同,就会对应不同的 localStorage。例如:
http://example.com
https://example.com
https://www.example.com
https://example.com:8443
假设用户支付前访问的是:
https://www.example.com/checkout
支付后返回的却是:
https://example.com/payment/result
即使这两个域名展示的是同一个网站,它们仍然属于两个 origin。
可以分别在支付前后的页面输出以下信息:
console.table({
href: window.location.href,
origin: window.location.origin,
protocol: window.location.protocol,
hostname: window.location.hostname,
port: window.location.port,
storageLength: window.localStorage.length
});
重点比较 window.location.origin,不要只看页面路径。
统一域名、HTTPS 和端口
整个支付流程最好只使用一个规范地址,例如:
https://www.example.com
服务端应将其他入口永久重定向到这个地址:
http://example.com/* -> https://www.example.com/*
https://example.com/* -> https://www.example.com/*
传给 AliPay 的同步返回地址也要使用固定的绝对 URL,不要根据未经校验的请求头临时拼接:
const returnUrl = 'https://www.example.com/payment/alipay/return';
如果应用部署在反向代理之后,还要确认代理正确传递了协议和主机信息。错误的 Host、X-Forwarded-Host 或 X-Forwarded-Proto 可能让应用生成错误的返回地址。
检查 Clear-Site-Data 响应头
服务器、CDN、反向代理或认证系统可能在返回页面的响应中发送:
Clear-Site-Data: "storage"
这个响应头会要求支持它的浏览器删除当前 origin 对应的存储数据,其中可能包括 localStorage。不同浏览器和版本的处理方式可能不同,所以即使问题只出现在 Chrome 中,也不能排除这个原因。
可以按以下步骤在 Chrome DevTools 中排查:
- 打开 Network 面板。
- 勾选 Preserve log。
- 完成一次支付跳转。
- 查看返回本站后的所有响应。
- 在 Response Headers 中搜索
Clear-Site-Data。 - 同时确认是否发生了额外的
301、302、307或308跳转。
如果这个响应头不是有意设置的,应从应用、网关、CDN 或认证中间件中移除。普通的支付返回页面不应发送清理整个站点存储的指令,这类操作通常只适合登录退出等场景。
排查前端代码是否主动清理
搜索项目中所有可能清理存储的代码:
localStorage.clear();
localStorage.removeItem('key');
window.localStorage.clear();
支付返回页、登录状态初始化、异常恢复和版本升级相关代码尤其值得检查。例如,支付跳转期间登录信息过期时,下面的逻辑可能清除全部数据:
if (!currentUser) {
localStorage.clear();
}
更安全的做法是只删除确定需要失效的键:
if (!currentUser) {
localStorage.removeItem('userProfile');
localStorage.removeItem('checkoutDraft');
}
还要检查键名是否冲突。如果多个应用共用同一个 origin,又都使用过于通用的键名,就可能互相覆盖数据:
localStorage.setItem('cache', JSON.stringify(data));
可以给键名加上业务前缀:
localStorage.setItem('shop.checkoutDraft', JSON.stringify(data));
为什么更换网络后可能恢复正常
网络本身通常不会直接操作浏览器的 localStorage,但办公室网络可能改变整个访问链路,例如:
- DNS 将域名解析到另一套服务器或旧环境;
- HTTP 代理修改重定向地址或响应头;
- 网关将 HTTPS 降级为 HTTP;
- 内部门户或认证代理插入额外跳转;
- 不同出口访问了不同的 CDN、负载均衡节点或应用版本;
- 代理缓存返回了错误的跳转响应。
可以在办公室和家庭网络中分别记录完整的跳转链,然后进行比较:
console.log({
url: location.href,
origin: location.origin,
referrer: document.referrer,
time: new Date().toISOString()
});
还可以在 Chrome DevTools 的 Network 面板中导出 HAR,对比两种网络环境下的请求 URL、状态码和响应头。HAR 可能包含 Cookie、查询参数或支付信息,对外分享前必须脱敏。
如果办公室网络只能通过 HTTP 访问某个中间页面,网络设备还可能修改未加密的响应。因此,支付流程和所有返回地址都应强制使用 HTTPS。
排除 Chrome 本机因素
如果两种环境中的 URL、跳转链和响应头完全一致,再检查 Chrome 本身:
- 使用无痕窗口测试,但无痕存储在窗口关闭后本来就会被删除;
- 暂时禁用隐私保护、缓存清理和安全类扩展;
- 使用新的 Chrome 用户配置文件测试;
- 检查浏览器是否由组织管理,以及是否配置了自动清理站点数据的策略;
- 在 DevTools 的 Application → Local Storage 中观察数据具体在哪一步消失;
- 确认是否打开了另一个 Chrome 配置文件或访客窗口。
一种可靠的定位方法是在支付前写入诊断值:
const diagnostic = {
id: crypto.randomUUID(),
origin: location.origin,
createdAt: new Date().toISOString()
};
localStorage.setItem(
'payment.storageDiagnostic',
JSON.stringify(diagnostic)
);
console.log(diagnostic);
支付返回后,再读取这个值并输出当前 origin:
const raw = localStorage.getItem('payment.storageDiagnostic');
console.log({
origin: location.origin,
diagnostic: raw ? JSON.parse(raw) : null
});
如果诊断值不存在,同时 origin 也发生了变化,基本可以确定是存储隔离导致的。如果 origin 相同,则继续检查 Clear-Site-Data、前端清理代码、扩展和浏览器策略。
不要依赖 localStorage 保存关键支付状态
即使最终确认了浏览器之间的差异,订单号、支付金额和支付结果也不应只保存在 localStorage 中。用户可以删除或修改这些数据,浏览器策略也可能随时使其不可用。
建议采用以下流程:
- 发起支付前,由服务端创建订单。
- 将不可猜测的订单引用传入支付流程。
- 服务端接收并验证
AliPay的异步通知。 - 浏览器返回后,根据订单引用向服务端查询真实的支付状态。
localStorage只保存界面草稿等允许丢失的数据。
示意代码:
async function loadPaymentResult(orderId) {
const response = await fetch(
`/api/orders/${encodeURIComponent(orderId)}/payment-status`,
{
credentials: 'same-origin',
headers: {
Accept: 'application/json'
}
}
);
if (!response.ok) {
throw new Error(`Failed to load payment status: ${response.status}`);
}
return response.json();
}
浏览器重定向参数只能用来定位订单,不能直接作为支付成功的可信依据。最终状态应以服务端验签后的支付通知或服务端主动查询结果为准。
备注:内容仅供参考。
