存储中的平台
Safari 对脚本可写存储的 7 天上限
发布于 更新于
智能跟踪防护(ITP)会在用户连续七个 Safari 使用日没有与某站点交互之后,删除该站点全部由脚本写入的存储。计数只看使用 Safari 的天数,不按日历算。主屏幕 Web App 在 Safari 之外运行,使用自己的计数器,所以同一个站点可能在标签页里丢失数据,在已安装应用里却仍然保留。
工作原理
链接到“工作原理”一节这项上限分两步到位,真正波及 PWA 离线数据的是第二步。
七天到底在数什么
链接到“七天到底在数什么”一节ITP 2.1 于 2019 年 2 月 21 日随 iOS 12.2 与 Safari 12.1 的测试版公布,将持久化客户端 Cookie 的有效期上限设为七天。这包括所有用 document.cookie 创建的持久 Cookie。WebKit 解释,跟踪方已经开始往第一方 Cookie 存储中写入标识符;在第一方上下文运行的跟踪脚本可以相互读写数据。
2020 年 3 月 24 日那篇介绍 iOS 与 iPadOS 13.4、macOS 上 Safari 13.1 中 ITP 的文章,把同一限制扩展到其余由脚本写入的存储,原因是第三方脚本已转向 LocalStorage 这类第一方存储,而后者根本没有过期机制。它对触发条件的措辞很精确:在连续七个 Safari 使用日没有与站点发生用户交互之后,删除该站点全部由脚本写入的存储。不打开 Safari,使用日就不增加;与站点交互则会重置计数。
这项上限删除哪些存储
链接到“这项上限删除哪些存储”一节WebKit 列出了受影响的脚本可写存储形式,并排除了若干遗留的网站数据类型:
| 存储形式 | PWA 会失去什么 |
|---|---|
| Indexed DB | 结构化的应用数据,包括离线队列与已同步的记录。 |
| LocalStorage | 放在同步存储里的偏好、特性开关与会话线索。 |
| SessionStorage | 被删除源的按标签页状态。 |
| Media keys | 为加密媒体保存的密钥。 |
| Service Worker 注册与缓存 | 注册本身连同它的 Cache Storage 内容。下一次访问没有既有控制者;只有页面再次调用 register() 并创建新注册后,新 Worker 才进入安装阶段。 |
这次清理会影响离线功能。如果应用外壳放在 Cache Storage,记录放在 IndexedDB,两者就会连同 Service Worker 注册一起删除。
主屏幕 Web App 为何单独计数
链接到“主屏幕 Web App 为何单独计数”一节WebKit 在 2020 年那篇文章里同时交代了已安装应用。七天上限的触发条件是连续七个 Safari 使用日没有与站点发生用户交互,而这说的是 Safari 里的情形;添加到主屏幕的 Web App 不属于 Safari,因而有自己的使用天数计数器,该计数与 Web App 的实际使用相符,打开它就会重置计时。WebKit 表示,不预期删除这类 Web App 的第一方数据,并请开发者在遇到这种情况时按严重缺陷上报,因为删除 Web App 中第一方的数据并非智能跟踪防护的本意。
平台覆盖范围
链接到“平台覆盖范围”一节七天上限只是存储逐出的一种原因。按 WebKit 2023 年的存储策略,超过总配额、系统存储紧张或用户已有一段时间未与站点交互,都可能触发删除,无需用户或站点发起。最后一种就是 ITP 的清理路径。
默认情况下,所有源都使用尽力而为模式。同一篇文章说明,清理时仍有活动页面,或者存储已切换到持久化模式的源,可能免于逐出。
持久化模式可以从脚本申请。该文章说明,源可以用 StorageManager.persisted() 查询模式、用 StorageManager.persist() 请求切换;自 Safari 17.0 以及 iOS 17、iPadOS 17 与 macOS Sonoma 上的 WebKit 应用起,Storage API 获得完整支持;WebKit 依据「站点是否作为主屏幕 Web App 打开」这类启发式规则决定是否授予请求。文章还提到,独立运行的 Web App(iOS 上的主屏幕 Web App,或 macOS 上添加到程序坞的 Web App)与在浏览器应用中打开时拥有相同的源配额与总配额,而 Cookie 与 HTTP 缓存不受这套管辖存储 API 的配额策略约束。
Service Worker 注册缺失时可以重新注册;申请持久化存储也可能让源免于逐出,具体条件见 WebKit 的说明。
在 Service Worker 注册缺失时恢复
链接到“在 Service Worker 注册缺失时恢复”一节WebKit 说明七天清理包含 Service Worker 注册和缓存。注册缺失并不能说明它为何不存在,因此下面的示例把这种状态作为重新注册并获取引导数据的条件,而不把它当成 ITP 清理发生过的证据。
async function reseedFromServer() { const response = await fetch('/api/bootstrap'); localStorage.setItem('bootstrap', await response.text());}
async function restoreMissingRegistration() { if (!('serviceWorker' in navigator)) { return 'unsupported'; } const registration = await navigator.serviceWorker.getRegistration(); if (!registration) { await navigator.serviceWorker.register('/sw.js'); await reseedFromServer(); return 'missing-registration-reseeded'; } return 'intact';}
restoreMissingRegistration().then((state) => console.log('存储状态:', state));该分支处理文档记录的删除机制,但不把注册缺失当作其原因的证据。WebKit 的文章描述的是客户端网站数据删除,而不是服务端账号删除。
从已安装的应用申请持久化模式
链接到“从已安装的应用申请持久化模式”一节WebKit 的启发式规则偏向从主屏幕打开的 Web App,因此在应用安装后申请持久化,比首次在标签页访问时申请更合适。函数会返回申请结果,调用后要检查是否获准。
async function ensurePersistentStorage() { if (!('storage' in navigator) || !navigator.storage.persist) { return false; // 没有 Storage API:按会被删除来规划 } if (await navigator.storage.persisted()) { return true; } return navigator.storage.persist(); // 可能解析为 false,两种结果都要处理}返回 false 表示源仍处于尽力而为模式,数据必须能从服务端重建。返回 true 也不等于豁免。2023 年那篇文章的措辞是:源的存储处于持久化模式时「可能」被排除在逐出之外,这是一项条件而不是保证;该文讲的是存储策略,并没有对七天清理作出任何承诺,所以上一个示例里的恢复路径无论如何都值得保留。
更正(2026-10-20)
链接到“更正(2026-10-20)”一节本次修订把第一方 Cookie 表述限定为跟踪脚本,不再把 Service Worker 注册缺失视为 ITP 清理的证据,并删除无来源支持的系统设置测试步骤与账号丢失结论。
另请参阅
链接到“另请参阅”一节- Full Third-Party Cookie Blocking and More(webkit.org)
- Intelligent Tracking Prevention 2.1(webkit.org)
- Updates to Storage Policy(webkit.org)
- Service Workers:
register()(W3C) - 逐出与尽力而为存储
- StorageManager.persist() 与 persisted()
- iOS 添加到主屏幕
规范
| 规范 | 状态 |
|---|---|
| 无。 | |