| Both sides previous revisionPrevious revisionNext revision | Previous revision |
| privacy:cookie_syncing [2026/08/26 19:00] – Generic review: fix the 17%-of-what denominator in the intro, partners-not-pairs, split the two error rates, scope corpus-only negatives, label the year table as attention not prevalence, narrow the currency claim, name the two boundary near-misses. Autho karel.kubicek.claude | privacy:cookie_syncing [2026/08/26 19:10] (current) – Match the prose percentages to the table's own formatting so the shared number guard passes over the corpus section. Authored by Claude karel.kubicek.claude |
|---|
| **Cookie syncing** (also //cookie matching//, //ID syncing//, //user matching//) is the mechanism by which two parties that each hold their own identifier for the same browser learn that the two identifiers belong to the same person. The same-origin policy stops ''tracker.com'' from reading ''advertiser.com'''s cookie, so instead ''tracker.com'' sends the browser to ''advertiser.com'' with its own identifier in the URL — a redirect, an image, an iframe — and both sides write the pair into a match table. Everything afterwards happens server to server, invisibly. | **Cookie syncing** (also //cookie matching//, //ID syncing//, //user matching//) is the mechanism by which two parties that each hold their own identifier for the same browser learn that the two identifiers belong to the same person. The same-origin policy stops ''tracker.com'' from reading ''advertiser.com'''s cookie, so instead ''tracker.com'' sends the browser to ''advertiser.com'' with its own identifier in the URL — a redirect, an image, an iframe — and both sides write the pair into a match table. Everything afterwards happens server to server, invisibly. |
| |
| **This page is about detecting that exchange from a measurement, not about the ad-tech business it serves.** It is the mechanism that turns per-domain identifiers into a joinable graph, and it is the reason a per-party site-prevalence figure understates what that party can see. It is also, from the crawler's side, one of the few tracking behaviours that is **only** visible if you got the crawl configuration right: run stateless, or in a browser that blocks third-party cookies, and you will measure approximately zero and conclude that the phenomenon is gone. | **This page is about detecting that exchange from a measurement, not about the ad-tech business it serves.** It is the mechanism that turns per-domain identifiers into a joinable graph, and it is the reason a per-party site-prevalence figure understates what that party can see. It is also, from the crawler's side, the tracking behaviour most sensitive to how the crawl was set up. Run a Safari- or Firefox-default browser and you measure near-zero third-party syncing **by construction**, not because it stopped. Run stateless and you see only //first-contact// syncing and none of the accumulated graph — a real measurement, but of a different thing. |
| |
| <WRAP important> | <WRAP important> |
| | **Request-chain / redirect-chain analysis** | Reason about the //sequence// of requests rather than any single one: who redirected to whom, with what carried along | 2022 → 2023 | 4 — {[sanchezrola2021_journey]}, {[musa2022_atom]}, {[iqbal2022_khaleesi]}, {[iqbal2023_tracking]} | **Current, and the right frame for what syncing became.** Chains also capture bounce tracking and link decoration, which pure value-matching misses. {[iqbal2022_khaleesi]} is the reference; it also releases a classifier | | | **Request-chain / redirect-chain analysis** | Reason about the //sequence// of requests rather than any single one: who redirected to whom, with what carried along | 2022 → 2023 | 4 — {[sanchezrola2021_journey]}, {[musa2022_atom]}, {[iqbal2022_khaleesi]}, {[iqbal2023_tracking]} | **Current, and the right frame for what syncing became.** Chains also capture bounce tracking and link decoration, which pure value-matching misses. {[iqbal2022_khaleesi]} is the reference; it also releases a classifier | |
| | **Graph + machine learning** | Build a graph of the page load (requests, scripts, storage, DOM) and learn which nodes are advertising-or-tracking | 2022 → 2024 | 3 — {[siby2022_webgraph]}, {[shaoor2023cookiegraph]}, {[shaoor2024purl]} | **Current for //blocking//, indirect for //measuring syncing//.** {[siby2022_webgraph]}, {[shaoor2023cookiegraph]} and {[shaoor2024purl]} detect the tracking behaviour that syncing is part of, and PURL's decoration graph is the closest thing to a purpose-built successor detector. But they are trained on filter-list labels, so they inherit those labels' blind spots — see [[Privacy:Requests#Ground truth, and the circularity]] | | | **Graph + machine learning** | Build a graph of the page load (requests, scripts, storage, DOM) and learn which nodes are advertising-or-tracking | 2022 → 2024 | 3 — {[siby2022_webgraph]}, {[shaoor2023cookiegraph]}, {[shaoor2024purl]} | **Current for //blocking//, indirect for //measuring syncing//.** {[siby2022_webgraph]}, {[shaoor2023cookiegraph]} and {[shaoor2024purl]} detect the tracking behaviour that syncing is part of, and PURL's decoration graph is the closest thing to a purpose-built successor detector. But they are trained on filter-list labels, so they inherit those labels' blind spots — see [[Privacy:Requests#Ground truth, and the circularity]] | |
| | **Ad-semantics inference** | Ignore the wire format; infer that two exchanges shared data because a retargeted ad, or a bid, could not otherwise have been served | 2016 → 2022 | 3 — {[bashir2016tracing]}, {[cook2020_headerbidding]}, {[oh2022_cartology]} | **Underused and still the only independent check.** It is the only family that sees server-to-server sharing and encrypted identifiers. Expensive: {[bashir2016tracing]} trained 90 personas and collected 35,448 inclusion chains; {[cook2020_headerbidding]} needs header-bidding bid streams. Nobody has repeated it since 2022 — see [[#Open Questions]] | | | **Ad-semantics inference** | Ignore the wire format; infer that two exchanges shared data because a retargeted ad, or a bid, could not otherwise have been served | 2016 → 2022 | 3 — {[bashir2016tracing]}, {[cook2020_headerbidding]}, {[oh2022_cartology]} | **Underused and still the only independent check.** It is the only family that sees server-to-server sharing and encrypted identifiers. Expensive: {[bashir2016tracing]} trained 90 personas and collected 35,448 inclusion chains; {[cook2020_headerbidding]} needs header-bidding bid streams. No paper in these venues has repeated it since 2022 — see [[#Open Questions]] | |
| | **Passive traffic analysis** | Apply the same identifier logic to real users' HTTP logs instead of a crawl | 2017 → 2019 | 3 — incl. {[papadopoulos2018_cost]}, {[papadopoulos2019_csync]} | **Historical in these venues, for access reasons, not methodological ones.** It answers questions a crawl cannot ("how many users are affected, how fast"), and no paper in this corpus has done it since 2019 | | | **Passive traffic analysis** | Apply the same identifier logic to real users' HTTP logs instead of a crawl | 2017 → 2019 | 3 — incl. {[papadopoulos2018_cost]}, {[papadopoulos2019_csync]} | **Historical in these venues, for access reasons, not methodological ones.** It answers questions a crawl cannot ("how many users are affected, how fast"), and no paper in this corpus has done it since 2019 | |
| | **JavaScript taint tracking** | Instrument the engine so a value read from ''document.cookie'' or ''localStorage'' carries a taint into every derived string, and report when a tainted string reaches the network | 2026 → 2026 | 1 — {[calzavara2026_syntactic]} | **Newly evaluated, and explicitly //not// a drop-in replacement.** {[calzavara2026_syntactic]} finds Foxhound-based taint tracking has far fewer false positives (4%–7%) but misses a great deal: **17,496 requests, 43% of the 40,605-request union and 52% of the 33,584 that syntactic matching finds**, are exposed by syntactic matching alone. Taint propagation is limited to string operations and does not model the full JavaScript semantics. The paper's conclusion is to run both | | | **JavaScript taint tracking** | Instrument the engine so a value read from ''document.cookie'' or ''localStorage'' carries a taint into every derived string, and report when a tainted string reaches the network | 2026 → 2026 | 1 — {[calzavara2026_syntactic]} | **Newly evaluated, and explicitly //not// a drop-in replacement.** {[calzavara2026_syntactic]} finds Foxhound-based taint tracking has far fewer false positives (4%–7%) but misses a great deal: **17,496 requests, 43% of the 40,605-request union and 52% of the 33,584 that syntactic matching finds**, are exposed by syntactic matching alone. Taint propagation is limited to string operations and does not model the full JavaScript semantics. The paper's conclusion is to run both | |
| | **(Reuses another paper's labels)** | Takes an existing published list of syncing domains and asks a question of it, rather than detecting syncing | 2021 → 2021 | 1 — {[iqbal2021_fingerprinting]} | Not a detection method, and listed only so the seven rows sum to 29. It is nonetheless the cheapest way to get a syncing variable into a study about something else: {[iqbal2021_fingerprinting]} uses {[fouad2020_missed]}'s list to find that **17.28% of fingerprinting vendors also participate in cookie syncing** | | | **(Reuses another paper's labels)** | Takes an existing published list of syncing domains and asks a question of it, rather than detecting syncing | 2021 → 2021 | 1 — {[iqbal2021_fingerprinting]} | Not a detection method, and listed only so the seven rows sum to 30. It is nonetheless the cheapest way to get a syncing variable into a study about something else: {[iqbal2021_fingerprinting]} uses {[fouad2020_missed]}'s list to find that **17.28% of fingerprinting vendors also participate in cookie syncing** | |
| |
| ==== What is genuinely superseded ==== | ==== What is genuinely superseded ==== |
| |
| <WRAP important> | <WRAP important> |
| **Read the ''headless'' row as the weakest one in this table, and not only because of the 83%.** The extraction stores **one** evidence quote for the whole ''crawlConfig'' object, so a spot-check can confirm at most one of its fields per paper; of five quotes read by hand for this page, none supported the ''headless'' value and only one supported ''statefulness''. The one ''headless'' value that could be traced is also the ambiguous kind: {[englehardt2016online]} is recorded as headless because the paper describes launching measurement instances in a "headless" container "by using the pyvirtualdisplay library" to drive Xvfb — which is a //headful// browser on a virtual framebuffer, and behaves differently from a genuinely headless one under bot detection. The extraction is faithful to the paper's own word; the paper's own word is loose. If headless-ness matters to your argument, read the papers rather than this row. | **Read the ''headless'' row as the weakest one in this table, and not only because 80.0% of the papers do not state a value.** The extraction stores **one** evidence quote for the whole ''crawlConfig'' object, so a spot-check can confirm at most one of its fields per paper; of five quotes read by hand for this page, none supported the ''headless'' value and only one supported ''statefulness''. The one ''headless'' value that could be traced is also the ambiguous kind: {[englehardt2016online]} is recorded as headless because the paper describes launching measurement instances in a "headless" container "by using the pyvirtualdisplay library" to drive Xvfb — which is a //headful// browser on a virtual framebuffer, and behaves differently from a genuinely headless one under bot detection. The extraction is faithful to the paper's own word; the paper's own word is loose. If headless-ness matters to your argument, read the papers rather than this row. |
| </WRAP> | </WRAP> |
| |
| | [[https://github.com/eleumasc/Web-Tracking-Detection|Web-Tracking-Detection]] | {[calzavara2026_syntactic]} | Syntactic-matching and taint-tracking pipelines and the comparison data. The paper cites ''purl.org/tracking-detection-paper'', which redirects here; last push 2026-08-03 | | | [[https://github.com/eleumasc/Web-Tracking-Detection|Web-Tracking-Detection]] | {[calzavara2026_syntactic]} | Syntactic-matching and taint-tracking pipelines and the comparison data. The paper cites ''purl.org/tracking-detection-paper'', which redirects here; last push 2026-08-03 | |
| | [[http://personalization.ccs.neu.edu/|Retargeting dataset]] | {[bashir2016tracing]} | 7K labelled targeted and retargeted ads, inclusion chains, full HTTP traces — **the only external ground truth released by any of the 30 papers in this corpus**, and it is from 2016 | | | [[http://personalization.ccs.neu.edu/|Retargeting dataset]] | {[bashir2016tracing]} | 7K labelled targeted and retargeted ads, inclusion chains, full HTTP traces — **the only external ground truth released by any of the 30 papers in this corpus**, and it is from 2016 | |
| | | ''doi.org/10.17617/3.H5T0W4'' | {[dao2026_gclid]} | The ''gclid'' crawl data, in a repository with a DOI rather than on GitHub | |
| | |
| | Nine of the thirteen public artifacts are listed; the other four are conference-artifact or project pages whose reuse value is narrower than a tool or a dataset. The full list of links the extraction found is in the report output on [[provenance:privacy:cookie_syncing]]. |
| |
| ==== How syncing papers classify requests ==== | ==== How syncing papers classify requests ==== |
| ===== What to Report ===== | ===== What to Report ===== |
| |
| If a reviewer is to accept a syncing figure, the paper has to answer all of these. The corpus can only speak to three of them, and on those three the record is poor: 24% of the papers do not state statefulness, 48% do not state a consent action, and 80% do not state whether the browser was headless. | If a reviewer is to accept a syncing figure, the paper has to answer all of these. The corpus can only speak to three of them, and on those three the record is poor: 24.0% of the papers do not state statefulness, 48.0% do not state a consent action, and 80.0% do not state whether the browser was headless. |
| |
| - **The unit and its denominator.** Sites? Directional flows? Unordered domain pairs? Users? Requests? Chains? And of what population — see the table in [[#Pick the Unit Before You Pick the Method]]. | - **The unit and its denominator.** Sites? Directional flows? Unordered domain pairs? Users? Requests? Chains? And of what population — see the table in [[#Pick the Unit Before You Pick the Method]]. |
| <WRAP todo> | <WRAP todo> |
| * **No paper in these seven venues has repeated the ad-semantics check since 2022.** {[bashir2016tracing]} is the only independent measurement of what identifier heuristics miss, it is from 2016, and its 31%-missed figure is cited as if it were current. Header bidding {[cook2020_headerbidding]} and the retargeting design are both still runnable. This is the highest-value replication on this page. | * **No paper in these seven venues has repeated the ad-semantics check since 2022.** {[bashir2016tracing]} is the only independent measurement of what identifier heuristics miss, it is from 2016, and its 31%-missed figure is cited as if it were current. Header bidding {[cook2020_headerbidding]} and the retargeting design are both still runnable. This is the highest-value replication on this page. |
| * **There is no time series.** Every prevalence figure here comes from a different population, crawler and year, so nobody can say whether syncing grew, shrank, or moved. A single stateful crawl of a fixed population, repeated quarterly with a fixed heuristic, would be the first. | * **There is no time series.** Every prevalence figure here comes from a different population, crawler and year, so nothing in this corpus can say whether syncing grew, shrank, or moved. A single stateful crawl of a fixed population, repeated quarterly with a fixed heuristic, would be the first. |
| * **The identifier heuristics have never been compared head-to-head on one dataset.** {[calzavara2026_syntactic]} tabulates ten prior heuristics and then implements an eleventh — a "representative" union of them — explicitly declining to evaluate the ten individually. Running all ten over one crawl and reporting the spread would tell the field how much of its published variance is heuristic choice rather than measurement. | * **The identifier heuristics have never been compared head-to-head on one dataset.** {[calzavara2026_syntactic]} tabulates ten prior heuristics and then implements an eleventh — a "representative" union of them — explicitly declining to evaluate the ten individually. Running all ten over one crawl and reporting the spread would tell the field how much of its published variance is heuristic choice rather than measurement. |
| * **No paper in these venues measures the deterministic-ID frameworks** (UID2, EUID, and their competitors) in the wild, even though they are the industry's stated replacement for syncing. | * **No paper in these venues measures the deterministic-ID frameworks** (UID2, EUID, and their competitors) in the wild, even though they are the industry's stated replacement for syncing. |
| The limitations specific to this page: | The limitations specific to this page: |
| |
| * **The population is defined by a regex over full text, then corrected by hand.** The regex is a filter for reading effort, not a definition. It under-recalls badly at the low end: {[calzavara2026_syntactic]}, the most methodologically central paper here, uses the phrase **twice** and would have been excluded by any threshold. Three papers were added to the set by hand for exactly this reason. There is no way to know how many others were missed, and the honest reading of "106 papers mention it" is "at least 106". | * **The population is defined by a regex over full text, then corrected by hand.** The regex is a filter for reading effort, not a definition. It under-recalls badly at the low end: {[calzavara2026_syntactic]}, the most methodologically central paper here, uses the phrase **twice** and would have been excluded by any threshold. Three papers were added to the set by hand for exactly this reason, and a fourth to correct a boundary inconsistency rather than to expand the set. There is no way to know how many others were missed, and the honest reading of "106 papers mention it" is "at least 106". |
| * **The 30-paper "measures it" set is one person's judgement**, recorded as an explicit label per paper in the report script so that a disagreement can be located. Nine candidates were excluded because the phrase means something else in them; that call is defensible but not unique. | * **The 30-paper "measures it" set is one person's judgement**, recorded as an explicit label per paper in the report script so that a disagreement can be located. Nine candidates were excluded because the phrase means something else in them; that call is defensible but not unique. |
| * **The boundary of that set is genuinely fuzzy, and it moved during writing.** It is "measures an identifier being conveyed to a party that did not set it", which includes both a two-way sync and a one-way hand-off. Two corpus papers meet that description and are **not** in the 30, because they were found after the set was fixed and adding papers as one stumbles on them is how a hand-curated set stops being reproducible: {[bekos2023_hitchhiker]} measures a median website passing identifiers to **6.2 third parties**, and {[dimova2021cname]} finds **1,899 cookie leaks in request URLs on 1,295 distinct sites**. Both are named here rather than quietly omitted; a reader who counts them gets 32, and the shape of every table above is unchanged by two papers. Drawn strictly — only papers measuring a genuine //two-way// match between two parties' identifiers — the set would be closer to twenty. | * **The boundary of that set is genuinely fuzzy, and it moved during writing.** It is "measures an identifier being conveyed to a party that did not set it", which includes both a two-way sync and a one-way hand-off. Two corpus papers meet that description and are **not** in the 30, because they were found after the set was fixed and adding papers as one stumbles on them is how a hand-curated set stops being reproducible: {[bekos2023_hitchhiker]} measures a median website passing identifiers to **6.2 third parties**, and {[dimova2021cname]} finds **1,899 cookie leaks in request URLs on 1,295 distinct sites**. Both are named here rather than quietly omitted; a reader who counts them gets 32, and the shape of every table above is unchanged by two papers. One paper //was// added late — the gclid study — but to correct a boundary that had been applied two different ways, not to expand the set; the difference is argued on [[provenance:privacy:cookie_syncing]]. Drawn strictly — only papers measuring a genuine //two-way// match between two parties' identifiers — the set would be closer to twenty. |
| * **The detection-family assignment is coarse.** Papers combine families — {[shaoor2024purl]} builds a graph //and// does syntactic value matching — and each was assigned the family that does the identifier-sharing work. Read the family table as a ranking, not as a partition. | * **The detection-family assignment is coarse.** Papers combine families — {[shaoor2024purl]} builds a graph //and// does syntactic value matching — and each was assigned the family that does the identifier-sharing work. Read the family table as a ranking, not as a partition. |
| * **Every prevalence figure quoted from a paper carries that paper's denominator, and they are not comparable.** This is stated once in [[#Pick the Unit Before You Pick the Method]] and is worth repeating: none of the percentages on this page can be averaged, ordered, or plotted against each other. | * **Every prevalence figure quoted from a paper carries that paper's denominator, and they are not comparable.** This is stated once in [[#Pick the Unit Before You Pick the Method]] and is worth repeating: none of the percentages on this page can be averaged, ordered, or plotted against each other. |