为何Safari 11在5秒内连续80次请求serviceWorker.js致服务器拥堵?
先还原下你遇到的异常场景:5秒内收到80次来自iPhone Safari 11.3的/serviceWorker.js请求,全部返回304缓存命中,且请求的referrer居然是脚本本身——这明显是不符合预期的异常行为。我之前排查Safari兼容性问题时碰到过几乎一模一样的情况,给你梳理几个核心原因和对应的解决方向:
my.domain.com:443 x.x.x.x - - [11/Apr/2018:06:08:18 +0200] "GET /serviceWorker.js HTTP/2.0" 304 0 "https://my.domain.com/serviceWorker.js" "Mozilla/5.0 (iPhone; CPU iPhone OS 11_3 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/11.0 Mobile/15E148 Safari/604.1"
1. 注册逻辑的异常触发
最常见的问题是Service Worker的注册代码被错误地重复执行,或者注册参数设置不合理:
- 首先要确认:注册Service Worker的代码只在HTML页面中执行,绝对不能写在
serviceWorker.js本身里(脚本被加载后不会在页面上下文运行,反而可能触发异常循环)。 - 检查注册时机:不要把注册代码放在
scroll、resize这类高频触发的事件回调里,也不要在页面的子框架中重复注册。正确的做法是只在页面加载完成后执行一次:
if ('serviceWorker' in navigator) { window.addEventListener('load', () => { navigator.serviceWorker.register('/serviceWorker.js') .then(reg => console.log('SW注册成功:', reg)) .catch(err => console.error('SW注册失败:', err)); }); }
- 关注referrer异常:你的请求referrer是脚本URL本身,这说明可能有用户直接访问了
/serviceWorker.js,而如果服务器误把HTML内容(包含注册代码)返回给了脚本请求,就会陷入“加载脚本→执行注册→再加载脚本”的死循环。要确保服务器对该请求只返回纯JS脚本,不会返回HTML内容。
2. Safari 11.3 的缓存处理bug
这是Safari 11.x版本的已知兼容性问题:当Service Worker脚本返回304缓存响应时,Safari可能会错误地认为脚本需要重新验证,从而无限重复发起请求。解决办法:
- 调整缓存头设置:给
serviceWorker.js设置稳定的缓存策略,推荐配置:Cache-Control: max-age=0, must-revalidate:强制每次请求都验证缓存,但确保验证通过后浏览器能正确使用缓存- 确保
ETag是基于文件内容生成的稳定值(不要用动态生成的ETag,比如包含时间戳),这样当文件未修改时,服务器返回304后,Safari能正确识别并停止重复请求
- 避免使用
no-cache或no-store这类过于严格的缓存策略,会加剧Safari的重复请求问题。
3. 过度频繁的更新检查
如果你的代码里手动调用了registration.update()来强制检查Service Worker更新,且调用频率过高(比如每秒一次),Safari 11的bug会让这个请求变成无限循环:
- 尽量依赖浏览器默认的更新机制:浏览器会自动每天检查一次Service Worker更新,或者在用户刷新页面时检查
- 如果必须手动检查,设置合理的间隔(比如5分钟以上),不要在短时间内重复调用
update()
4. 临时降级方案
如果以上方法都无法快速解决,可以针对Safari 11.x用户做降级处理:
- 通过用户代理检测识别Safari 11,暂时不注册Service Worker
- 提示用户升级到Safari 12及以上版本(Apple在后续版本中修复了这个Service Worker的循环请求bug)
内容的提问来源于stack exchange,提问作者JOHO

