Stateful tracking is the oldest form of web tracking and still the most common: a third party embedded on many sites stores an identifier in your browser on the first one and reads it back on every other. Mayer and Mitchell call this the core of third-party tracking[1]. Every major browser now defends against it with some form of partitioning: state written by a third party is filed under the site you were visiting at the time, and is invisible when the same third party appears on another site or is visited directly. This area tests whether those partitions hold. It carries of the 100 points.
How the cross-site checks are set up
A tracker needs two vantage points to link visits, and Bye Spy recreates both. The test page (byespy.heysec.com) embeds its test tracker (byespy.heysec.eu) in an invisible frame, like a social widget or an ad. The frame writes a random identifier everywhere it can. Then a small window opens on the tracker's own site as a top-level page, the way you would visit the network's own website after reading a page with its widget on it, and tries to read the identifier back. The two halves report to the server under a random run ID, and the page compares them.
The tracker is on a different registrable domain from the test page, as a real tracker is: a different "site" in the browser's sense, decided with the Public Suffix List. Subdomains of one domain are the same site, and browsers rightly share state between them.
Third-party cookies
What Bye Spy measures
From the test page, a credentialed request to the tracker sets two cookies with random values, both Secure; SameSite=None; HttpOnly and ten minutes long: an ordinary one (bs_tp) and a partitioned one (bs_tp_part, with the CHIPS Partitioned attribute). Then the check looks both ways:
- Forward: when the tracker's own site opens as a top-level page, does the browser send either cookie set in the embedded context?
- Reverse: the tracker's own site sets a first-party cookie (
bs_tp_fp). Back on the test page, is that cookie sent to the embedded tracker?
Either direction is enough to link the two contexts. Whether the partitioned cookie was accepted is reported as a fact but not scored: a partitioned cookie is exactly what a browser should allow.
Why it matters
A third-party cookie is one identifier attached to every request a browser makes to the tracker's domain, from any site. The tracker's server needs no script at all to recognise you; it reads the Cookie header.
GET /pixel.gif?page=news.example%2Fpolitics HTTP/1.1
Host: tracker.example
Cookie: uid=7f3a9c21e0b4 ← the same value arrives from every site that embeds tracker.example
HTTP/1.1 200 OK
Set-Cookie: uid=7f3a9c21e0b4; Max-Age=34560000; Path=/; Secure; SameSite=None
uid.Third-party cookies also make cookie syncing possible: a redirect from one ad system to another passes the first system's ID to the second, so that partners can join their profiles.
GET https://adx.example/sync?partner=dsp Cookie: adx_uid=7f3a9c21e0b4
302 Location: https://dsp.example/match?adx_uid=7f3a9c21e0b4 ← the DSP files it next to its own cookie
A partitioned (CHIPS) cookie keeps the convenience of third-party state, a chat widget's session for example, without the tracking. The browser stores it under the pair (top-level site, cookie's site), so the widget gets a different jar on each site:
Set-Cookie: __Host-session=e1d2; Secure; Path=/; SameSite=None; Partitioned
Evidence and prevalence
Englehardt and Narayanan's 2016 crawl of a million sites found cookie syncing "pervasive": 45 of the 50 most common third parties synced identifiers with at least one other, and doubleclick.net alone shared 108 different cookies with 118 other parties[2]. A year-long study of real users' traffic found 97% of regular users exposed to cookie syncing, which multiplied the number of companies that knew a typical user's identifier by 6.75[3]. In the HTTP Archive's 2025 crawl of over 16 million sites, doubleclick.net set third-party cookies on 20% of desktop pages, down from 27% a year earlier[4].
Browsers diverged. Safari blocks all third-party cookies by default since March 2020 (Safari 13.1)[5]. Firefox gives every site its own cookie jar, Total Cookie Protection, by default since version 103 (July 2022)[6]. Chrome planned to phase third-party cookies out, then announced in July 2024 that it would not, and in April 2025 that it would not show a prompt either, keeping them on by default behind a setting[7][8]. Partitioned cookies are supported in Chrome since version 114, Firefox since 141 and Safari since 26.2[9].
How it is scored
- Safe
- Neither direction worked: the embedded tracker's cookie went into a separate jar for this site, or was refused.
- Leaks
- A cookie crossed between the embedded tracker and its own site, in either direction.
A yes-or-no check, and the heaviest in its area: it tests the most widely deployed cross-site identifier there is, and the end result directly, one value readable in two contexts.
Third-party storage
What Bye Spy measures
Cookies are only one place to keep an identifier. The embedded tracker frame writes the run's random token through five other mechanisms, and the tracker's own window tries to read it:
| Mechanism | The frame | The tracker's own window |
|---|---|---|
| localStorage | stores the token under a fixed key | reads the key |
| IndexedDB | stores it in a database | opens the database without creating one |
| Cache storage | puts a response holding it | looks the response up |
| BroadcastChannel | listens on a named channel and answers "ping" with the token | sends "ping" and waits a second |
| SharedWorker | starts a named worker and hands it the token | connects to the same name and asks for it |
The last two are not storage but live channels: they work while the frame is still open, as it is in a real tab next to the tracker's own site.
Why it matters
Blocking third-party cookies alone does not stop tracking if any of these is shared. Tracking scripts have long written their identifier to several places at once and restored it from whichever survived, a technique known as respawning[10].
// In the tracker's iframe on news.example:
const uid = localStorage.getItem('uid') ?? crypto.randomUUID();
localStorage.setItem('uid', uid);
new BroadcastChannel('uid').onmessage = (e) => e.target.postMessage(uid);
// On tracker.example itself, or its iframe on another site, while that tab is still open:
const ch = new BroadcastChannel('uid');
ch.onmessage = (e) => report(e.data); // the same uid, across sites
ch.postMessage('ping');
Evidence and prevalence
Samy Kamkar's evercookie (2010) demonstrated the idea by writing one identifier to more than a dozen browser stores, including local storage, IndexedDB, window.name, ETags and the cache[11]. Acar and colleagues then measured respawning by real trackers on popular sites[10]. Browsers now partition all of these by top-level site: Chrome since version 115 (2023), including BroadcastChannel and SharedWorker[12]; Firefox as part of its state partitioning, on by default since version 103[13]; and Safari for local storage, IndexedDB, service workers and caches, with BroadcastChannel partitioned since 2021[14][15]. A failure here today usually means an old browser.
How it is scored
- Safe
- Every mechanism kept a separate copy per site, or refused the embedded tracker outright.
- Partly leaks
- Some mechanisms were shared. The value is the share kept separate: four of five gives 0.8.
- Leaks
- Every mechanism carried the token across.
A mechanism the browser does not support, or whose read-back failed with an error, is left out of the fraction rather than counted either way. One shared mechanism is already enough to carry an identifier, so the fraction is generous; it rewards browsers that are nearly there.
Shared browser cache
What Bye Spy measures
The embedded tracker frame fetches a small file from its server twice. The file is cacheable for ten minutes, and the server counts how often it is actually fetched. Two requests and one count proves the browser caches it. Then the tracker's own window requests the same file. If the server's count stays at one, the window was served from the cache filled in the embedded context: one cache for both sites. If it rises to two, the cache is partitioned.
Why it matters
Whatever the cache returns, a script can read, and a cached response can hold an identifier: in its body, or in its ETag, which the browser returns on revalidation.
GET /i.js HTTP/1.1 first visit, any site
HTTP/1.1 200 OK
ETag: "7f3a9c21e0b4" ← an ID, not a version
GET /i.js HTTP/1.1 later, on another site
If-None-Match: "7f3a9c21e0b4" ← the browser hands the ID back
HTTP/1.1 304 Not Modified
Evidence and prevalence
In 2011, Ayenson and colleagues found an analytics service, KISSmetrics, respawning identifiers on hulu.com from ETags, and the same ETag following users to other sites using the service, with private browsing and Do Not Track on[16]. The class action that followed was settled in 2012, with the company agreeing not to use ETags or similar techniques without notice and choice[17]. Cache partitioning closes the channel: Safari first partitioned its cache in 2013[18], Chrome keys its cache by top-level site and frame site since version 86 (2020)[19], and Firefox partitions its network state, including the HTTP cache, since version 85 (2021)[20].
How it is scored
- Safe
- The tracker's own site had to fetch the file again: a separate cache per site. Also safe when the browser did not reuse the file even within the test page (a private window, or caching off), as nothing can be carried.
- Leaks
- The file came from the cache the embedded tracker filled.
Weighted below cookies and storage because a cache identifier is harder to keep alive: caches are evicted, and revalidation is easy to disturb.
Interest topics (Topics API)
What Bye Spy measures
Whether document.browsingTopics() exists, and whether it returns topics. The call is made with skipObservation: true, so that the test does not itself add to your topics.
Why it matters
The Topics API was Chrome's proposed replacement for third-party cookies in advertising: the browser itself infers your interests from the sites you visit and hands a few of them to any embedded script that asks.
const topics = await document.browsingTopics();
// [{ topic: 239, taxonomyVersion: "2", modelVersion: "4", configVersion: "chrome.2", version: "chrome.2:2:4" }, …]
// 239 is a category from the published taxonomy; the ad server reads it as an interest.
Topics are coarse and change weekly, so they are not a stable identifier on their own. But they are profile data you did not choose to share, and a few topics combined with other signals narrow you down.
Evidence and prevalence
In October 2025 Google announced it was retiring most Privacy Sandbox technologies, Topics among them, citing low adoption[21]. The API was deprecated in Chrome 144 and is being removed progressively from Chrome 150 on, with calls rejecting and later reaching a stub. The removal was still rolling out in autumn 2026[22]. Firefox and Safari never implemented it.
How it is scored
- Safe
- The API is absent or retired, or the browser refused to answer.
- Partly leaks (0.5)
- The API answered with no topics. That happens when it is switched off in settings, or when the asking site is not enrolled with Google, and a page cannot tell which.
- Leaks
- The browser returned one or more topics.
Low weight: it is coarse, and is disappearing. It stays in the test while browsers that answer it are still in use.
Long-lived analytics cookies
What Bye Spy measures
The test page sets a first-party cookie from a script (document.cookie) asking for 400 days, then reads its expiry back through the Cookie Store API and deletes it.
Why it matters
This one is first-party: analytics and advertising tags set their identifier as the site's own cookie, which survives third-party cookie blocking, and ask for it to last years. Every return visit is then linked to the first, and the cookie is what server-side integrations attach to conversions.
// What an analytics tag writes on the site's own domain:
document.cookie = '_ga=GA1.1.1234567890.1700000000; Max-Age=63072000; Path=/; Domain=.shop.example'; // two years
Evidence and prevalence
The HTTP Archive found Google Analytics' _ga cookie as a first-party cookie on 49% of desktop pages in 2024[23]. Safari caps cookies set from scripts at seven days since ITP 2.1 (2019), and at one day when the visitor arrived through a decorated link from a known tracker[24][25]. Chrome caps every cookie at 400 days since version 104 (2022), following the draft cookie specification[26]. Brave capped script-set cookies at seven days from 2019 until August 2025, when it moved to six months[27].
A limit of this check. A page can only read a cookie's expiry through the Cookie Store API, and Safari (18.4 and later) and Firefox (140 and later) implement it without the expiry field[28]. In those browsers this check shows Can't measure and drops out of the score, even though Safari is the one browser that caps these cookies. In practice the check scores Chromium-based browsers.
How it is scored
- Safe
- The cookie was granted eight days or less.
- Leaks
- It was granted more, typically the full 400 days.
The line is drawn just above Safari's seven-day cap. Low weight, because the identifier is first-party: it links your visits to one site, not your visits across sites, unless the site passes it on.
References
- J. R. Mayer and J. C. Mitchell. "Third-Party Web Tracking: Policy and Technology." IEEE Symposium on Security and Privacy, 2012. doi:10.1109/SP.2012.47
- S. Englehardt and A. Narayanan. "Online Tracking: A 1-million-site Measurement and Analysis." ACM CCS, 2016. cs.princeton.edu/~arvindn/publications/OpenWPM_1_million_site_tracking_measurement.pdf
- P. Papadopoulos, N. Kourtellis, E. P. Markatos. "Cookie Synchronization: Everything You Always Wanted to Know But Were Afraid to Ask." The Web Conference (WWW), 2019. arXiv:1805.10505
- HTTP Archive. Web Almanac 2025, Privacy chapter. almanac.httparchive.org/en/2025/privacy
- J. Wilander. "Full Third-Party Cookie Blocking and More." WebKit blog, 24 March 2020. webkit.org/blog/10218
- Mozilla. "Firefox rolls out Total Cookie Protection by default to all users worldwide." 14 June 2022; Firefox 103.0 release notes. blog.mozilla.org/…, firefox.com/en-US/firefox/103.0/releasenotes
- A. Chavez. "A new path for Privacy Sandbox on the web." Google, 22 July 2024. privacysandbox.google.com/blog/privacy-sandbox-update
- A. Chavez. "Next steps for Privacy Sandbox and tracking protections in Chrome." Google, 22 April 2025. privacysandbox.google.com/blog/privacy-sandbox-next-steps
- Google. "Cookies Having Independent Partitioned State (CHIPS)"; Mozilla, Firefox 141.0 release notes; WebKit, "WebKit features for Safari 26.2". privacysandbox.google.com/cookies/chips, firefox.com/…/141.0/releasenotes, webkit.org/blog/17640
- G. Acar, C. Eubank, S. Englehardt, M. Juarez, A. Narayanan, C. Diaz. "The Web Never Forgets: Persistent Tracking Mechanisms in the Wild." ACM CCS, 2014. doi:10.1145/2660267.2660347
- S. Kamkar. evercookie, 2010. samy.pl/evercookie, github.com/samyk/evercookie
- Google. "Storage partitioning." privacysandbox.google.com/cookies/storage-partitioning
- MDN. "State Partitioning." developer.mozilla.org/en-US/docs/Web/Privacy/Guides/State_Partitioning
- WebKit. "Tracking Prevention in WebKit." webkit.org/tracking-prevention
- WebKit changeset 282366, BroadcastChannel partitioning, September 2021. trac.webkit.org/changeset/282366/webkit
- M. Ayenson, D. J. Wambach, A. Soltani, N. Good, C. J. Hoofnagle. "Flash Cookies and Privacy II: Now with HTML5 and ETag Respawning." 2011. ssrn.com/abstract=1898390
- MediaPost. "KISSmetrics Finalizes Supercookies Settlement." 2012. mediapost.com/publications/article/191409/…
- WebKit Bugzilla. Bug 110269, "Optionally partition cache to prevent using cache for tracking." 2013. bugs.webkit.org/show_bug.cgi?id=110269
- Chrome for Developers. "Gaining security and privacy by partitioning the cache." developer.chrome.com/blog/http-cache-partitioning
- Mozilla Security Blog. "Firefox 85 Cracks Down on Supercookies." 26 January 2021. blog.mozilla.org/security/2021/01/26/supercookie-protections
- A. Chavez. "Update on plans for Privacy Sandbox technologies." Google, 17 October 2025. privacysandbox.google.com/blog/update-on-plans-for-privacy-sandbox-technologies
- blink-dev. "Intent to Deprecate and Remove: Topics API." 2025–2026. groups.google.com/a/chromium.org/g/blink-dev/c/_R85yctz4Rs
- HTTP Archive. Web Almanac 2024, Privacy chapter. almanac.httparchive.org/en/2024/privacy
- J. Wilander. "Intelligent Tracking Prevention 2.1." WebKit blog, 21 February 2019. webkit.org/blog/8613
- J. Wilander. "Intelligent Tracking Prevention 2.2." WebKit blog, 24 April 2019. webkit.org/blog/8828
- Chrome for Developers. "Cookie Expires and Max-Age attributes now have upper limit." 2022. developer.chrome.com/blog/cookie-max-age-expires
- Brave. Issues #3443 (2019) and #48063 (2025). github.com/brave/brave-browser/issues/3443, github.com/brave/brave-browser/issues/48063
- Can I use, "Cookie Store API"; MDN, Firefox 140 release notes. caniuse.com/cookie-store-api, developer.mozilla.org/en-US/docs/Mozilla/Firefox/Releases/140