同源多应用部署踩坑记:Service Worker 把子路径「吃」掉了

举报
yd_285894438 发表于 2026/10/21 12:00:52 2026/10/21
【摘要】 两个应用挂在同一域名不同路径,页面却打不开?记录一次 Service Worker 导航兜底导致的路径劫持排查,给出定位思路与三种隔离方案。

一、现象

服务端一切正常:本机 curl 带鉴权返回 200,页面 15KB 完整,外网经代理访问同样 200,标题、按钮、脚本都在。但浏览器里打开 https://HOST:8443/app/,看到的却是另一个应用(一套笔记工具)的界面,控制台没有任何报错。

更奇怪的是,换浏览器无痕窗口就正常,普通窗口一直坏。

二、排查顺序

先把"服务挂了"这个假设排除掉,按链路从内到外逐段验证:

  • 本机回环直连,带 Basic Auth,看状态码与响应体长度;
  • 指定 --resolve 强制走本机,排除 DNS 与 CDN;
  • 外网经代理再请求一次,确认边界一致;
  • 最后在浏览器里读 document.title 与首屏 DOM,而不是看截图。

前三条全绿,问题就锁定在浏览器侧。

三、根因:Service Worker 的导航兜底

同一个域名下,根路径部署了一个 PWA。它的 Service Worker 注册在根作用域,fetch 事件里对 mode === "navigate" 的请求做了离线兜底:只要网络失败或匹配到缓存策略,就返回缓存的 index.html。

问题在于缓存键匹配写得太宽——用 caches.match(event.request) 命中不到时,直接 return caches.match("/")。于是 /app/ 这种子路径导航请求,被换成了笔记应用的外壳。服务端 200 的响应根本没机会渲染。

这也解释了为什么无痕正常:无痕窗口不共享 Service Worker 注册。

四、快速确认方法

在问题页面执行:

navigator.serviceWorker.getRegistrations()
  .then(rs => rs.map(r => [r.scope, r.active && r.active.scriptURL]))

如果 scope 是根路径、脚本是你另一个应用的 sw.js,基本可以定案。再配合 DevTools → Application → Service Workers 勾选 Bypass for network,页面立刻恢复,就是它。

五、三种解法

1. 改匹配逻辑(治本,但要改代码)

导航兜底只对应用自己的路径生效,其它路径直接走网络:

self.addEventListener("fetch", (e) => {
  if (e.request.mode !== "navigate") return;
  const url = new URL(e.request.url);
  if (!url.pathname.startsWith("/notes/")) return; // 只兜自己的路径
  e.respondWith(
    fetch(e.request).catch(() => caches.match("/notes/index.html"))
  );
});

2. 独立 origin(治本,最省事)

给第二个应用单独的子域名和证书,作用域天然隔离,互不干扰。缺点是 DNS、证书、Caddy/Nginx 站点各配一份。

3. 临时绕过(治标)

让用户清一次该站点的存储,或在部署里给 /sw.js 返回 Cache-Control: no-store 并注销旧注册。只能救急,下次发版还会复现。

六、小结

同一个 origin 下挂多个应用时,Service Worker 的作用域是整个 origin,不是路径。任何在根路径注册了 SW 的应用,都可能把兄弟路径的导航请求"吃掉"。排查时先分清"服务端是否正常"和"浏览器拿到的是不是服务端的响应",再去看 SW、缓存和重定向,能省掉大量无效改动。

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。