Service Worker 的 fetch 事件能拦截同源且页面主动发起的请求(如 fetch、XMLHttpRequest、img、script、link 等),但不拦截 iframe 加载、跨域无 CORS 资源、sendBeacon 及 SW 自身安装请求。

Service Worker 的 fetch 事件能拦截哪些请求?
它能拦截页面发起的所有同源(或满足 CORS)的 fetch 和 XMLHttpRequest 请求,包括 HTML、JS、CSS、WASM、JSON、图片、字体,甚至你用 fetch('/data.bin') 下载的二进制文件。但注意:iframe 加载的文档、<script></script> 标签的初始加载、导航请求(如地址栏跳转)默认不经过 SW,除非你在 register 时显式设置 scope 覆盖对应路径。
哈希校验必须在 fetch 响应体读取完成后才能执行
因为校验依赖完整字节流,而 Response.body 是一个 ReadableStream,只能被消费一次。如果你在 SW 中先调用 response.clone(),再分别用于计算哈希和构造新响应,是可行的;但若直接读取原 response.body,后续页面就拿不到数据了。
- 必须用
response.arrayBuffer()或response.blob()获取全部内容,再传给哈希函数(如crypto.subtle.digest('SHA-256', buffer)) - 不能用
response.text()处理二进制文件,会破坏原始字节(尤其含非 UTF-8 字节时) - 大文件需注意内存压力:浏览器对 SW 中的 ArrayBuffer 有隐式限制,超 100MB 可能触发
RangeError或静默失败
如何把预期哈希值安全地注入 Service Worker?
不能硬编码在 sw.js 里——每次文件更新都得手动改 SW,且哈希本身可能被篡改。推荐方式是让服务端在返回资源时,通过 HTTP 响应头携带校验信息:
- 使用标准头
Content-Digest: sha-256=:47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=:(RFC 9530),但目前 Chrome 尚未支持自动校验 - 更现实的做法:服务端对每个静态资源生成
.sha256同名文件(如app.js.sha256),SW 在 fetch 时额外发一次请求获取该哈希值 - 或者,在主页面 HTML 中以
<meta name="file-integrity" content="app.js:abc123..."></meta>注入,再由 SW 通过self.clients.matchAll()找到控制中的 client 页面,用postMessage拿到映射表
无论哪种,都要确保哈希源本身可信——即该元数据也必须走 HTTPS,且最好由服务端签名验证。
校验失败时,fetch 事件里怎么处理才不影响用户体验?
不能简单抛错或返回空响应,否则页面 JS 报错、图片不显示、应用直接白屏。你应该:
- 记录错误到
console.error并上报监控(如fetch('/log', {method:'POST', body: JSON.stringify({url, error: 'hash-mismatch'})})) - 尝试从缓存中 fallback:比如先查
caches.match(event.request),若命中且之前已校验过,可直接返回 - 若缓存也失效,再返回一个带明确提示的降级响应,例如:
new Response('文件完整性校验失败,请刷新重试', {status: 500}) - 避免重试无限循环——可在 request URL 上加查询参数标记(如
?integrity-checked=1),SW 检测到该参数则跳过校验
真正难的是边界情况:比如用户离线时缓存了旧版文件,但服务端已发布新版并更新了哈希清单——此时 SW 需区分“本地缓存损坏”和“版本过期”,不能一概而论为失败。

















