无痕窗口不仅会隐藏浏览历史,还会改变存储类 API 的行为方式,而这种差异正是网站借以判断你是否处于隐身模式的关键所在。
隐身/无痕浏览窗口承诺的是:网站无法得知你之前来过。但它并没有承诺网站无法察觉你当下正处于这样的窗口中——十年来,出版商、广告网络和反欺诈工具正是利用了这个缺口。因为私密窗口在底层的行为必然与普通窗口不同——不同的存储限额、不同的磁盘写入方式——一段探测这些差异的脚本往往可以在不读取任何一个 Cookie 的情况下,就有把握地得出"这是一个私密窗口"的结论。
核心要点
- 无痕浏览隐藏的是历史记录,而不是行为——像
navigator.storage.estimate()这样的存储 API,在普通会话和私密会话之间仍然会报告出真实且可测量的差异。 - Chromium 最古老的一招是测量对(如今已废弃的)FileSystem API 的写入速度:在隐身模式下它运行在内存中,速度大约是普通模式下真实磁盘写入的 3 到 4 倍。
- Chrome 在 2019 年堵上这个漏洞之后,检测脚本转而利用 Chrome 通过
navigator.storage.estimate()报告的存储配额——在隐身模式下这个值更小、更趋于一致,在普通模式下则更大、与磁盘容量成比例。 - 此后 Chrome 开始在所有模式下都报告一个固定的"可预测"配额,专门用来消灭这一检测思路——这是一项仍在推进中的修复,而非已经尘埃落定的方案。
- Firefox 和 Safari 各自也有一段私密模式"露馅"的历史,主要集中在
localStorage和IndexedDB在私密会话中表现不同(甚至直接失效)。 - BrowserInsight 的指纹检测会展示你的浏览器当前暴露出的存储、Canvas 等各类信号——无论是否处于私密窗口。
网站为何要费心检测隐身模式
两大主要动机都是商业性的,而不是安全意义上的对抗性行为。采用限额付费墙的出版商——最著名的是《纽约时报》——利用隐身模式检测,来阻止读者每次打开一个新的私密窗口就重置免费文章阅读次数。广告技术和反欺诈工具则把它当作又一个信号:私密窗口流量中,与广告欺诈刷量团伙、优惠券/促销滥用相关联的比例明显偏高,因此把它标记出来,会像机器人检测系统综合多个弱信号而非依赖单一信号那样,汇入一个更宏观的风险评分。
这两种用途都不需要知道你是谁——只需要知道你当前使用的窗口是不是私密的。正是这个更狭窄的目标,让这项技术有别于浏览器指纹识别:它不是要构建一个持久化的身份标识符,而只是要在一次页面加载期间回答一个是/否问题。
FileSystem 写入速度技巧
最早一种可靠的隐身模式检测技术,瞄准的是 Chrome 对(如今已废弃、仅 Chromium 支持的)File and Directory Entries API的实现——也就是 window.webkitRequestFileSystem 及相关接口。在普通窗口中,Chrome 会用磁盘上的真实文件来支撑这个 API;而在隐身模式下,为了在窗口关闭后不留下任何痕迹,Chrome 转而用一个内存中的虚拟文件系统来支撑它。
内存的速度远远快于磁盘,因此一段脚本只需写入一批临时文件、计时写入耗时,就能得到一个干净的信号:如果写入速度比普通模式下的基准快三到四倍,就说明文件系统压根没有碰过磁盘,也就意味着这是一个私密窗口。全程无需权限弹窗、无需用户交互——只是针对一个当时默认就已暴露的 API 做一次计时测量。
Chrome 在 76 版(2019 年)堵上了这个具体漏洞,把隐身模式下 FileSystem 的实现方式也改成写入磁盘而非内存,从而抹平了这个计时差异。这是对付费墙检测这一使用场景引发主流媒体关注之后的直接回应——是一次真正意义上的漏洞修复,而不是表面功夫。
StorageManager 配额技巧
这次修复并没有终结这场猫鼠游戏,只是把它转移到了另一个 API 上。如今每一款现代浏览器都提供 navigator.storage.estimate(),它是 Storage API 的一部分,会返回一个 Promise,解析出当前源(origin)的存储 usage(用量)与可用 quota(配额):
// Illustrative sketch — not the exact detectIncognito heuristic
async function looksLikeIncognito() {
const { quota } = await navigator.storage.estimate();
const ONE_TWENTY_MB = 120 * 1024 * 1024;
// Historically: Chromium capped Incognito quota well below
// the large, disk-proportional value normal windows report.
return quota < ONE_TWENTY_MB;
}
在普通浏览模式下,Chromium 过去会根据实际可用磁盘空间的一个比例来计算所报告的配额——通常是几十甚至几百 GB。而在隐身模式下,由于配置文件是临时性的,通常受限于可用内存(RAM)而非磁盘,返回的配额就会小得多,且在不同机器之间相对一致。脚本只需检查这个数值是否低于某个较低的阈值(公开的技术文章中提到的临界值大约是 120 MB),就能有把握地做出判断。
这套确切的判断逻辑——再加上针对 Chromium 的后备方案,以及分别面向 Firefox、Safari、Edge 和 Opera 的独立检测——被维护为一个开源参考实现,即 GitHub 上的 Joe12387/detectIncognito,这是了解这些检测手段如何随时间演变得愈发多样、愈发依赖具体浏览器的一份有用的一手资料。
Firefox 与 Safari 也各有破绽
Chromium 的存储配额缺口之所以最受关注,是因为 Chrome 的市场份额使它成为价值最高的目标,但 Firefox 和 Safari 在各自不同的阶段,也都出现过可被检测的私密模式怪癖:
| Browser | 历史上的私密模式破绽 | 根本原因 |
|---|---|---|
| Chrome / Chromium | 2019 年前:FileSystem 写入速度;2019 年后:存储配额 | 内存文件系统;基于 RAM 的更小配额 |
| Safari | localStorage.setItem() 会抛出配额超限错误 | 私密模式下 Web Storage 配额被限制为 0 字节 |
| Firefox | indexedDB 为 null,或请求静默失败 | 私密窗口中 IndexedDB 多年来一直不可用 |
Safari 的版本可以说是最粗糙的:多年来,在私密窗口内调用 localStorage.setItem() 会立即抛出异常,因为 Safari 把私密模式的存储配额直接设为零,而不是对其做隔离处理。只需用一个 try/catch 包住一次测试写入,就能以近乎完美的准确率检测出该模式。苹果在 Safari 11 中修复了这个问题,让 localStorage 在私密窗口中重新变得完全可写——数据只是存在内存中、随会话结束而消失,而不再是被直接拦下。
Firefox 的 IndexedDB 则从另一个方向讲述了类似的故事:私密窗口要么根本不暴露 indexedDB 对象,要么让打开请求失败,脚本可以像捕获 Safari 的存储异常那样轻松捕获这一点。此后 Firefox 转向了在私密浏览中支持 IndexedDB,做法是使用加密的、限定于会话范围的存储——这与 Chrome 堵上 FileSystem 缺口的思路如出一辙:让私密模式的实现足够接近普通模式,使得计时/可用性信号不复存在。
十年的猫鼠游戏
在每一款浏览器身上,模式都是相同的三步循环,只是时间线各不相同:某个实现细节泄露出私密模式的差异 → 这个差异被武器化,用于付费墙检测或反欺诈 → 厂商重新设计私密模式的实现来消除这个缺口,而这通常又会在别处打开一个更小、更隐蔽的新缺口。存储类 API 之所以始终是这场较量的战场,是因为私密模式的全部职责就是让数据表现得与平常不同——而这恰恰是检测脚本永远可以尝试测量的那个属性。
2026 年的现状
StorageManager 配额技巧在当前的 Chrome 稳定版上依然有效,但看起来时日无多。Chrome 一直在推行一项名为"可预测的报告存储配额"的缓解措施:对于没有获得更高存储权限的站点,它会在所有浏览模式下都报告一个人为设定的配额——用量加上 10 GiB 与你四舍五入后的磁盘容量二者中较小的那个——目的就是让普通窗口和隐身窗口在这项测量上变得无法区分。截至 2026 年年中,这项措施正在向存储权限有限的站点逐步推出;已被授予无限存储权限、或触发了强制配额上限的站点则明确不受影响,因此这一检测思路并没有在所有地方同时消失。
实际的启示是:没有任何一种单一的存储检测能够成为持久有效的隐身模式检测器,而且这些技术从来都不需要 Cookie、Canvas 或持久化标识符——相比指纹识别,这是一场更狭窄、节奏更快的博弈,是逐个 API 地较量,而不是逐个信号地较量。
隐身模式能防止指纹追踪吗?
单靠它自己不能,而一旦检测这个问题得到解答,这一点反而更加重要。私密窗口改变的是存储了什么——历史记录、Cookie、缓存的表单数据——而不是你的浏览器在实时页面上暴露了什么:无论窗口是否私密,同样的 Canvas 哈希、WebGL 渲染器字符串、字体和屏幕特征都会以完全相同的方式呈现出来。如果你使用隐身模式是期望借此在指纹追踪面前获得匿名性,浏览器指纹技术完全指南详细说明了这种期望为何站不住脚,以及真正能降低暴露程度的方法。
分别在普通窗口和私密窗口中各运行一次 BrowserInsight 的指纹检测——底层信号会几乎一模一样,这是理解"私密"与"不可指纹识别"是两种不同保证的最直观方式。
常见问题
网站是否总能识别出我在使用隐身模式?
不能。这类检测始终依赖于特定浏览器版本中的某个具体实现怪癖,而这些怪癖迟早会被修补。去年还稳定有效的一种技术,可能在浏览器更新后悄无声息地失效,反之亦然——这是一场不稳定的军备竞赛,而不是一项已成定局的能力。
清除 Cookie 或使用 VPN 能阻止隐身模式检测吗?
不能,因为这些技术从一开始就不看 Cookie 或你的 IP 地址。它们测量的是存储类 API 在当前会话中的行为方式,这与 VPN 或清除 Cookie 所改变的内容毫无关系。
检测隐身模式是否违法?
检测本身并不必然违法,但网站拿这个信息做了什么,可能引发另一层法律和伦理问题——尤其是当它被用来出于商业目的规避用户刻意做出的隐私选择时,比如强制重置付费墙计数。相关法规因司法辖区而异,本文内容不构成法律建议。
为什么 Chrome 花了这么多年才彻底解决这个问题?
因为每一次修复都只堵上了一个具体的漏洞,而那个根本性的约束——私密会话的存储必须在某处表现得与普通会话不同——不断在其他地方重新打开新的、更细微的缺口。2019 年的 FileSystem 修复并没有触及 StorageManager 配额缺口,而当前的配额缓解措施本身也仍在带着若干例外逐步推出,这很好地说明了要彻底做到"设计上无法区分"有多困难。
结语
隐身模式检测是浏览器指纹识别的一个更狭窄、节奏更快的近亲:它不是要从几十个信号中构建一个持久化标识符,而只是问一个问题——存储的表现是否像一个私密会话?——用当下任何能回答这个问题的 API 来作答。每一次修复都堵上一个真实的缺口,而每一个新缺口迟早又会被找到,这正是为什么理解其原理,比不假思索地相信某个"隐身模式无法被检测"的说法更重要。
推荐阅读:


