Syllabus Lesson 147 of 156 · Measure the web like a researcher
Measure the web like a researcher

Find hidden trackers

Why it matters The trackers that worry researchers most are the ones that hide: behind your own site's name, inside a redirect you never notice, or in a script that never sets a cookie at all. A simple third-party count misses them. This lab finds three kinds in your crawl data.

Think of a shop where the security guard wears the shop's own uniform but is really employed by someone else. A visitor counting "outside staff" would never spot them. Hidden trackers work like that.

1. CNAME cloaking

DNS: the internet's address book: it turns a name like m.calmriver.example into the address of a computer.

CNAME: a DNS record that says "this name is really another name". m.calmriver.example can be a CNAME for c.cloakco.example, so the request looks first party but reaches a tracker.

CNAME cloaking: a tracker hiding behind a site's own subdomain through a CNAME, so blocklists and third-party cookie rules see a first-party name.

In the crawl records, each request has cname: where DNS says the host really points, or None. A cloaked request has a host on the page's own site, and a CNAME on a listed tracker. Careful: many sites point subdomains at a content delivery network such as edge.cdn.example. That is normal, so check the CNAME's site against TRACKER_LIST.

Safari limits cookies set through CNAME-cloaked responses to 7 days, and Firefox lets blocking extensions look up CNAMEs to uncloak them; Chrome extensions cannot do that lookup (checked October 2026).

2. Bounce chains

When you click a link, it can pass through a tracker's redirect on the way, letting it set or read its own first-party cookie. You met bounce tracking in Leg 6; here you count it at scale. Each record has click_chain: the URLs the crawler passed through after clicking the page's first outbound link. A middle hop is a bounce if its site differs from both the starting site and the final site. A site redirecting through its own /out page is not a bounce.

3. Fingerprinting scripts

An instrumented crawler records which browser features each script used, in scripts. Following the approach of published studies, a script is suspicious when it both reads back a drawing or sound (CANVAS_READS) and reads at least 4 different device traits (TRAIT_APIS). A drawing app that only exports your picture uses the canvas but reads few traits, so it does not count.

from watched.leg7b_web import CANVAS_READS, TRAIT_APIS
apis = ["canvas.toDataURL", "navigator.userAgent", "screen.width",
        "navigator.languages", "Intl.timeZone"]
reads_canvas = any(a in CANVAS_READS for a in apis)
traits = {a for a in apis if a in TRAIT_APIS}      # a set: each trait once
print(reads_canvas, len(traits))                   # True 4

Watch for fingerprinting served from the site's own host. No third-party list would catch it; only behaviour does.

A common misconception: "If my blocker shows zero third-party trackers, nothing tracks me." Cloaked, bounced and first-party fingerprinting trackers all slip past a list of third-party names. Behaviour-based checks, like the ones you write here, are how browsers and researchers find them.

In other languages

The fingerprinting rule in JavaScript and PHP:

// JavaScript
function isFingerprinting(apis) {
  const readsCanvas = apis.some(a => CANVAS_READS.includes(a));
  const traits = new Set(apis.filter(a => TRAIT_APIS.includes(a)));
  return readsCanvas && traits.size >= 4;
}
// PHP
function is_fingerprinting(array $apis): bool {
    $reads = count(array_intersect($apis, CANVAS_READS)) > 0;
    $traits = count(array_unique(array_intersect($apis, TRAIT_APIS)));
    return $reads && $traits >= 4;
}

Recap

  • CNAME cloaking hides a tracker behind the site's own subdomain; check where the CNAME really points.
  • A bounce is a redirect hop on a site that is neither where you started nor where you ended.
  • Fingerprinting shows in behaviour: a canvas or audio readback plus many device traits.
  • Behaviour finds what name lists miss, including first-party fingerprinting.
Your turn

Write five functions over crawl records:

  1. cloaked_hosts(load, tracker_list): the sorted list of distinct request hosts on the page's own site whose cname is set and whose CNAME's site is in tracker_list.
  2. bounce_hops(load): from load["click_chain"], the sites of the middle hops that differ from both the first and the last URL's site (an empty list if the chain is shorter than 3).
  3. is_fingerprinting(apis): true if any API is in CANVAS_READS and at least 4 different APIs are in TRAIT_APIS.
  4. fingerprinting_scripts(load): the URLs of scripts in load["scripts"] that pass that test.
  5. hidden_report(loads, tracker_list): a dict counting sites with each kind: "cname_cloaking", "bounce", "fingerprinting" and "first_party_fingerprinting" (a fingerprinting script on the page's own site).
Then set report for the full crawl. Tests also crawl other simulated webs.

Spotted a problem in this lesson? Report it

Code · runs in your browser
Output