PAYATHON 2026

从 `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 中排查:

  1. 打开 Network 面板。
  2. 勾选 Preserve log。
  3. 完成一次支付跳转。
  4. 查看返回本站后的所有响应。
  5. 在 Response Headers 中搜索 Clear-Site-Data。
  6. 同时确认是否发生了额外的 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 中。用户可以删除或修改这些数据,浏览器策略也可能随时使其不可用。

建议采用以下流程:

  1. 发起支付前,由服务端创建订单。
  2. 将不可猜测的订单引用传入支付流程。
  3. 服务端接收并验证 AliPay 的异步通知。
  4. 浏览器返回后,根据订单引用向服务端查询真实的支付状态。
  5. 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();
}

浏览器重定向参数只能用来定位订单,不能直接作为支付成功的可信依据。最终状态应以服务端验签后的支付通知或服务端主动查询结果为准。

备注:内容仅供参考。