PAYATHON 2026

History.js 能否通过 pushState 跨 HTTP 和 HTTPS?

支付阿杰

结论

不能。history.pushState() 只能写入与当前页面同源的 URL。HTTP 和 HTTPS 属于不同源,即使域名和端口相同,协议不同也会触发浏览器的安全限制。

因此,在下面的 HTTP 页面中:

http://www.example.com/some/resource

不能直接执行:

history.pushState(
  null,
  null,
  'https://www.example.com/payment/for/some/resource'
);

History.js 无法绕过这项限制,传入完整 URL 也不会改变结果。

原因

浏览器通过以下三个部分判断两个地址是否同源:

  • 协议(scheme)
  • 主机名(host)
  • 端口(port)

下面两个地址使用了不同的协议,所以不属于同一个源:

http://www.example.com
https://www.example.com

pushState() 只会修改当前文档的历史记录和地址栏,并不会加载另一个页面。浏览器不允许 HTTP 文档借此把地址栏伪装成 HTTPS 页面,否则用户可能误以为当前内容已通过安全连接加载。

完整 URL 并非不能使用,但它必须与当前页面同源。例如,当前页面本身也位于相同的 HTTPS 源时,可以执行:

history.pushState(
  null,
  null,
  'https://www.example.com/payment/for/some/resource'
);

推荐方案:真正跳转到 HTTPS

进入支付页面时,应使用普通页面导航:

window.location.assign(
  'https://www.example.com/payment/for/some/resource'
);

也可以直接赋值:

window.location.href =
  'https://www.example.com/payment/for/some/resource';

浏览器会重新加载页面并建立真正的 HTTPS 连接。支付完成或退出支付流程后,再按业务需要导航到其他页面。

如果希望继续使用 AJAX 页面加载

可以让整个移动站点都运行在 HTTPS 下。这样,普通页面与支付页面属于同一个源,AJAX、History API 和页面切换动画仍可继续使用。

例如,当前页面是:

https://www.example.com/some/resource

此时可以先通过 AJAX 获取同源的支付页面,再更新历史记录:

const paymentUrl =
  'https://www.example.com/payment/for/some/resource';

$.ajax({
  url: paymentUrl,
  method: 'GET'
}).done(function (html) {
  $('#page-container').html(html);

  history.pushState(
    { page: 'payment' },
    '',
    paymentUrl
  );
});

同时还要处理浏览器的前进和后退操作:

window.addEventListener('popstate', function (event) {
  loadPage(window.location.href, {
    pushHistory: false
  });
});

pushState() 本身不会发起请求。页面内容加载、错误处理和界面更新仍由 AJAX 加载器负责。

如果暂时不能全站启用 HTTPS

可以把支付入口当作跨源边界。用户点击支付链接时,停止 AJAX 页面切换,改用完整跳转:

$(document).on('click', 'a.payment-link', function (event) {
  event.preventDefault();
  window.location.assign(this.href);
});

不要先通过 AJAX 获取 HTTPS 支付页面,再从 HTTP 页面调用 pushState()。即使服务器配置了 CORS,它也只能控制网络请求权限,无法解除 History API 的同源限制。

注意事项

  • History.js 只是对浏览器历史功能的封装,不能改变原生 History API 的安全规则。
  • 无论指定绝对 URL、相对 URL,还是更换 History.js 的调用方式,都无法通过 pushState() 从 HTTP 切换到 HTTPS。
  • 支付页面必须通过真正的 HTTPS 导航打开,不能只修改地址栏来表示连接已经安全。
  • 如果全站使用 HTTPS,应将 HTTP 请求重定向到对应的 HTTPS 地址,避免用户停留在不安全页面。
  • 支付流程还要正确处理会话 Cookie,通常应根据实际需求配置 Secure、HttpOnly 和 SameSite 属性。
  • 不建议将支付表单或敏感支付数据作为普通 AJAX 页面片段注入 HTTP 页面。只要外层文档仍是 HTTP,整个交互就不具备 HTTPS 页面应有的安全保障。

备注:内容仅供参考。