# Why Your Attribution Data Credits the Wrong Channels

The 30-40% of touchpoints client-side tracking loses is not a random sample. It skews toward Safari, ad-block, and consent-refusing users, and that changes which channel wins your budget review.

- Canonical: https://mbuzz.co/articles/attribution-data-biased-not-incomplete
- Published: 2026-07-23
- Author: Holly Mehakovic, mbuzz (https://mbuzz.co)

---

> **TL;DR:** Incomplete implies the missing data looks like the data you kept, so you could gross everything up by a factor and keep the same channel rankings. That is wrong. Client-side tracking fails hardest on three overlapping groups at once (Safari and iOS users, ad-block users, and consent-refusers), all of whom lean high-income and technical, and none of whom are spread evenly across your channels. So recovering the missing data does not just enlarge the totals, it re-ranks the channels. Server-side collection closes the observation gap and gives your model a representative sample. It does not make attribution causal, and it does not override a user's legal consent choice.


## Incomplete is a comfort. It is also usually wrong.

When a marketer hears that client-side tracking loses 30-40% of touchpoints, the word that gets used is "incomplete." It is a comforting word. It suggests the missing data looks like the data you kept, a smaller sample of the same picture. If that were true, your channel rankings would still hold. You could multiply everything by 1.4, shrug, and move on.

The assumption baked into that shrug is that the loss is random. It is not. The data you fail to capture is missing-not-at-random, which statisticians shorten to MNAR, and it goes missing in a specific, measurable direction. Client-side tracking fails hardest on three populations at once, and all three lean the same way.

The first is Safari and iOS users, who skew higher-income and more mobile than your Android and Chrome base. The second is ad-block users, who skew technical, educated, developer-heavy, and higher-earning. The third is consent-refusers, who skew privacy-conscious and, on EU traffic, are close to half your audience.

These groups overlap. An affluent iOS user running a content blocker who rejects your cookie banner is one person, dropped three times. And here is the part that matters for your budget: which channels reach these people is not uniform. Direct, organic, email, and affiliate deliver a very different mix of Safari, iOS, ad-block, and consent-refusing traffic than paid social or display does. So when you systematically under-count these users, you do not just shrink the totals. You shrink some channels more than others.

<div class="bg-slate-50 border-l-4 border-slate-400 p-5 my-8 rounded-r-md not-prose">
  <p class="text-xs font-bold text-slate-500 tracking-[0.15em] mb-3">THE ONE-SENTENCE VERSION</p>
  <p class="text-sm text-slate-700 leading-relaxed mb-3">Recovering the missing data does not change the size of the pie. It changes which slice wins.</p>
  <p class="text-sm text-slate-700 leading-relaxed">If the loss were random, server-side collection would be a rounding-error fix. Because the loss is biased, server-side changes the decision: which channel you fund next quarter.</p>
</div>

One boundary before we go further, because a measurement-literate reader will want it stated up front. Server-side collection closes the observation gap. It recovers touchpoints your client-side tags never saw. It does not make MTA causal. You still have a correlational path model. You have just stopped feeding it a skewed sample. Fixing the sample is necessary but not sufficient for truth, and the limitations section below is where that gets honest.

## The three biases, with sources

### Safari caps client-side cookies at seven days, and at 24 hours from an ad click

Safari's Intelligent Tracking Prevention caps the lifetime of any cookie set by JavaScript (`document.cookie`) at seven days. That seven-day cap arrived with ITP 2.1, released by WebKit on 21 February 2019, and was later extended to script-writable storage more broadly. When a visitor arrives from a URL carrying tracking parameters such as `gclid` or `fbclid`, the cap drops to 24 hours. That 24-hour link-decoration cap was introduced in ITP 2.2 and hardened in ITP 2.3, described in John Wilander's WebKit blog post on 23 September 2019.

The 24-hour cap fires specifically on paid-ad landings, because those are the URLs carrying `gclid` and `fbclid`. A returning Safari user who converts eight days after clicking your Google ad shows up in client-side attribution as a brand-new direct or organic session. That is not a random loss. It strips credit from paid and hands it to direct or organic, in one predictable direction.

This is not a niche browser problem. Safari holds roughly 56% of US mobile browser share as of 2024 per StatCounter (verify the live figure for your publish month before quoting it exactly). The point survives rounding. The ITP-throttled browser is the plurality of US mobile traffic, so the seven-day and 24-hour caps hit the majority of your mobile sessions.

### iOS users opt out of tracking at scale, and they skew affluent

On the app side, Apple's App Tracking Transparency prompt tells the same story about direction. Global ATT opt-in sat at roughly 50% as of Q1 2024, with the US at 44%, the UK at 46%, Germany at 47%, France at 53%, and Sweden lowest at 31%, per AppsFlyer's ATT data findings released on 26 April 2024. Other panels read lower. Adjust's Q2 2025 panel reported around 35% on average with wide variance by vertical and geo. The honest claim is that roughly a third to a half of iOS users opt in, depending on source and vertical, so roughly half are un-trackable at the ID level.

Keep this lane clean. ATT opt-out breaks device-graph and IDFA matching for app-install campaigns. It is not a direct claim about web cookie loss. It belongs here as corroboration that privacy-driven data loss is real and biased, not as a web-attribution mechanism.

The affluence skew is well-established across a decade of data, though the crispest dollar figures are dated. A decade ago, Comscore's US Mobile App Report from August 2014 found the median iPhone user earned about $85,000 against roughly $61,000 for Android, a 40% gap. That figure is over ten years old and should not be presented as current. The current anchor is Pew Research Center's "Americans' Use of Mobile Technology and Home Broadband," published 31 January 2024: 98% of adults in households earning $100,000 or more own a smartphone, against 79% of those under $30,000, and iPhone ownership concentrates at the top of the income and education distribution. The direction is confident. The old median is not worth leaning on.

### Ad-block users skew technical, educated, and high-income

Ad blockers hide an estimated 15-30% of traffic from analytics. Global ad-block users number somewhere above 900 million, close to 30% of internet users, in the PageFair and Blockthrough lineage of reports. The demographic skew is the part that matters here: usage concentrates among 18-to-34-year-olds, males, developers and IT professionals, the highly educated, and higher earners. The most defensible primary sources are Blockthrough's annual PageFair Adblock Report and GlobalWebIndex. Treat any precise figure, such as "half of developers block ads," as verify-before-publish. The pattern is solid; the decimals need a primary read.

The consequence is direct. Your developer-heavy and technical-buyer channels (Hacker News, developer newsletters, GitHub, technical SEO) deliver a much higher ad-block rate than your Facebook and Instagram traffic. Client-side tracking under-counts your best technical channels the most, so the channels that look weakest on a client-side dashboard can be the ones working hardest.

### EU consent refusal is a coin-flip when the banner is fair

When a "Reject all" button sits on equal footing with "Accept all," rejection runs around 50% and can exceed 60%. Aggregate 2024-25 consent studies put average acceptance in Europe at roughly 42-47%. US acceptance runs far higher, often above 80%, because banner requirements are laxer. These figures come from vendor-blog aggregations of consent-rate benchmarks, so verify the specific percentages against a named primary study before quoting decimals. The direction holds regardless. Refusers are privacy-conscious by definition, which makes EU consent loss missing-not-at-random by construction.

<div class="not-prose my-8 bg-white border border-slate-200 rounded-lg overflow-hidden">
  <div class="px-5 py-3 border-b border-slate-200 bg-slate-50">
    <p class="text-xs font-bold text-slate-500 tracking-[0.15em]">THREE LOSSES, ONE DIRECTION</p>
  </div>
  <div class="px-5 py-5">
    <p class="text-sm text-slate-700 mb-4"><strong class="text-slate-900">The question:</strong> is the 30-40% you lose a random sample, or does it lean one way?</p>
    <div class="grid grid-cols-1 sm:grid-cols-3 gap-3">
      <div class="bg-slate-50 border border-slate-200 rounded-md p-3">
        <div class="text-[10px] font-bold tracking-[0.15em] text-slate-500 mb-1">SAFARI &amp; iOS</div>
        <div class="text-2xl font-bold text-slate-900 mb-1">~56%</div>
        <div class="text-xs text-slate-500">of US mobile browsing, cookie-capped at 7 days (24h from ad clicks). Skews higher-income.</div>
      </div>
      <div class="bg-slate-50 border border-slate-200 rounded-md p-3">
        <div class="text-[10px] font-bold tracking-[0.15em] text-slate-500 mb-1">AD-BLOCK</div>
        <div class="text-2xl font-bold text-slate-900 mb-1">15-30%</div>
        <div class="text-xs text-slate-500">of traffic hidden from analytics. Skews developer, educated, high-earning.</div>
      </div>
      <div class="bg-slate-50 border border-slate-200 rounded-md p-3">
        <div class="text-[10px] font-bold tracking-[0.15em] text-slate-500 mb-1">EU CONSENT</div>
        <div class="text-2xl font-bold text-slate-900 mb-1">~50%</div>
        <div class="text-xs text-slate-500">reject a fair banner. Skews privacy-conscious by definition.</div>
      </div>
    </div>
    <p class="text-xs text-slate-500 mt-4">All three lean the same way, and all three overlap into roughly the same person. That is what makes the loss biased rather than incomplete. Figures are directional; verify current readings before quoting.</p>
  </div>
</div>

## What the bias does to a real budget review

The rest of the argument is easier to see through worked examples. These are illustrative models, constructed to show the mechanism. They are not measured case studies, and the numbers are chosen to make the effect legible, not reported from a live account.

### The Google Ads underpayment

A B2C brand runs Google Search and Meta. Safari is 56% of its mobile sessions. A Safari user clicks a Google ad, so a `gclid` lands in the URL and the cookie is capped at 24 hours. The user browses, leaves, and converts six days later from a bookmark.

<div class="not-prose my-8 flex flex-wrap items-stretch gap-2">
  <div class="flex-1 min-w-[120px] bg-indigo-50 border border-indigo-200 rounded-lg p-3">
    <div class="text-[10px] font-bold tracking-[0.15em] text-indigo-500 mb-1">DAY 1</div>
    <div class="text-sm font-semibold text-slate-900">Clicks Google ad</div>
    <div class="text-xs text-slate-500 mt-1">gclid in URL, cookie capped at 24h</div>
  </div>
  <div class="flex items-center text-slate-300 text-xl">&rarr;</div>
  <div class="flex-1 min-w-[120px] bg-slate-50 border border-slate-200 rounded-lg p-3">
    <div class="text-[10px] font-bold tracking-[0.15em] text-slate-500 mb-1">DAY 2</div>
    <div class="text-sm font-semibold text-slate-900">Cookie expires</div>
    <div class="text-xs text-slate-500 mt-1">gclid gone client-side</div>
  </div>
  <div class="flex items-center text-slate-300 text-xl">&rarr;</div>
  <div class="flex-1 min-w-[120px] bg-slate-50 border border-slate-200 rounded-lg p-3">
    <div class="text-[10px] font-bold tracking-[0.15em] text-slate-500 mb-1">DAY 7</div>
    <div class="text-sm font-semibold text-slate-900">Converts via bookmark</div>
    <div class="text-xs text-slate-500 mt-1">no surviving cookie</div>
  </div>
  <div class="flex items-center text-slate-300 text-xl">&rarr;</div>
  <div class="flex-1 min-w-[120px] bg-amber-50 border border-amber-200 rounded-lg p-3">
    <div class="text-[10px] font-bold tracking-[0.15em] text-amber-600 mb-1">CREDITED TO</div>
    <div class="text-sm font-semibold text-amber-700">Direct</div>
    <div class="text-xs text-amber-700 mt-1">Google gets zero</div>
  </div>
</div>

Client-side, the conversion lands with no surviving cookie and gets attributed to Direct. Google gets nothing. Server-side, a first-party server session (or a server-set cookie with a longer life) preserves the `gclid`, and the conversion is credited to Google Paid. Across a quarter, Google's contribution might read as 12% client-side against 19% server-side, while Direct shrinks from 28% to 21%. You were about to cut Google spend because it "was not converting." The channel that wins the budget review flips on the same conversions.

### The newsletter that looks like a phantom

A developer-tools company gets traffic from a technical newsletter and a Google Display campaign. The newsletter audience is around 45% ad-block and heavily Safari and iOS. The Display audience is around 12% ad-block and Chrome-heavy.

<div class="not-prose my-8 bg-white border border-slate-200 rounded-lg overflow-hidden">
  <div class="px-5 py-3 border-b border-slate-200 bg-slate-50">
    <p class="text-xs font-bold text-slate-500 tracking-[0.15em]">SAME CONVERSIONS, INVERTED RANKING</p>
  </div>
  <div class="px-5 py-5">
    <div class="grid grid-cols-2 gap-3">
      <div class="bg-slate-50 border border-slate-200 rounded-md p-3">
        <div class="text-[10px] font-bold tracking-[0.15em] text-slate-500 mb-2">CLIENT-SIDE VIEW</div>
        <ul class="text-sm text-slate-700 space-y-1">
          <li>Newsletter pixel fires for barely half its clicks</li>
          <li>Display fires for ~88%</li>
          <li class="pt-2 border-t border-slate-200 mt-2"><strong>Verdict:</strong> newsletter looks weak, Display looks efficient</li>
        </ul>
      </div>
      <div class="bg-emerald-50 border border-emerald-200 rounded-md p-3">
        <div class="text-[10px] font-bold tracking-[0.15em] text-emerald-700 mb-2">SERVER-SIDE VIEW</div>
        <ul class="text-sm text-slate-700 space-y-1">
          <li>Newsletter clicks resolve regardless of the blocker</li>
          <li>Its true contribution roughly doubles</li>
          <li class="pt-2 border-t border-emerald-200 mt-2 text-emerald-700"><strong>Verdict:</strong> newsletter is the efficient channel; Display was flattered by a low block rate</li>
        </ul>
      </div>
    </div>
    <p class="text-xs text-slate-500 mt-4">The ranking of the two channels inverts. The channel you would have killed is the one that earns the budget. Same total conversions, different winner. Illustrative model.</p>
  </div>
</div>

### The consent haircut that is not flat

An EU-heavy SaaS gets 50% consent rejection. The naive fix is "we see half the data, scale everything by two." But organic and direct visitors consent at a different rate than paid-social visitors, because privacy-conscious organic and direct audiences reject more. Suppose organic converters reject at 55% and paid-social converters reject at 40%. Scaling both by the same factor of two over-credits paid social and under-credits organic. Even a correct gross-up applied uniformly re-ranks the channels wrongly, because the missingness rate itself varies by channel.

This is the whole reason "incomplete" is the wrong mental model. You cannot fix missing-not-at-random data with a flat multiplier. Server-side collection, first-party and consent-respecting where required, removes the guess instead of scaling it.

## What server-side actually fixes, and what it does not

This audience will close the tab if the piece overclaims, so here are the limits, stated plainly rather than buried.

Server-side fixes observation bias, not causal inference. Recovering the missing touchpoints gives your model a representative sample instead of a skewed one. It does not turn MTA into an incrementality measurement. A last-touch or even a data-driven model on server-side data is still correlational. It tells you which touchpoints occurred, not which ones caused the conversion. This is consistent with how we think about measurement generally: MTA is one lens, and for real spend decisions you triangulate with marketing mix modeling, geo-lift, and surveys.

Server-side has its own coverage gaps and its own consent obligations. It does not legally let you ignore consent. In GDPR jurisdictions a refusing user's data still should not be processed for tracking without a lawful basis. Server-side wins on the technical losses (ITP caps, ad-block, tag failures), not by overriding a user's stated refusal. Be precise about which losses it recovers and which it must respect.

The counterargument to pre-empt is "just gross up by a factor." A sophisticated reader will say that if they know their Safari share, they can weight for it. The answer is that the weight varies by channel, by conversion lag, and by device simultaneously, and you do not observe the missing users' channel mix. That mix is exactly what is missing. A single correction factor assumes you already know the thing you cannot see.

Every magnitude here is directional, not exact. ATT opt-in runs 35-50%, ad-block hides 15-30%, consent refusal runs 40-60%. The thesis does not depend on any single number being precise. It depends on all three biases pointing the same way, which they do.

## A query you can run this week

If you want to check whether your own loss is biased rather than incomplete, do not start with a correction factor. Start by segmenting your observed conversions by the attributes that predict the loss, and look at whether the channel mix shifts across those segments. If Safari and mobile conversions credit direct and organic far more heavily than Chrome and desktop conversions do, your loss is directional, and a flat multiplier will not save you.

```sql
-- Does the channel mix shift with the attributes that predict client-side loss?
-- If the split differs sharply across these segments, your loss is biased, not random.
SELECT
  CASE
    WHEN browser = 'Safari' OR os_family = 'iOS' THEN 'safari_ios'
    ELSE 'chrome_android_other'
  END AS loss_segment,
  channel,
  COUNT(*) AS conversions,
  ROUND(
    100.0 * COUNT(*)
      / SUM(COUNT(*)) OVER (PARTITION BY
          CASE
            WHEN browser = 'Safari' OR os_family = 'iOS' THEN 'safari_ios'
            ELSE 'chrome_android_other'
          END),
    1
  ) AS pct_of_segment
FROM conversions
WHERE conversion_date >= CURRENT_DATE - INTERVAL '90 days'
GROUP BY 1, 2
ORDER BY 1, conversions DESC;
```

If the two `loss_segment` groups produce meaningfully different channel splits, the population you are losing does not look like the population you are keeping. That is the signal that server-side collection would move a decision, not just tidy a total.

## Further reading

- [Server-Side vs Client-Side Tracking](/articles/server-side-vs-client-side-tracking) — the mechanics of where the 30-40% goes
- [iPhone and iOS Tracking](/articles/iphone-ios-tracking-attribution) — how Apple's privacy changes reshape the sample
- [Why Platform Reports Don't Match](/articles/platform-reports-dont-match) — the double-counting problem that sits next to this one
- [MTA, MMM and Incrementality](/articles/mta-mmm-incrementality-triangulation) — why fixing the sample still is not causal proof

## Key takeaways

- Incomplete data is a random sample of the same picture; biased data is missing-not-at-random and skews in a known direction
- The three big client-side losses (Safari/iOS, ad-block, consent-refusal) overlap and all lean high-income and technical
- Because the loss varies by channel, a flat gross-up multiplier re-ranks your channels wrongly instead of correcting them
- Safari's 24-hour link-decoration cap strips paid-ad credit and hands it to direct/organic, a directional error not a random one
- Server-side fixes the observation bias, not the causal question; for spend decisions you still triangulate with MMM, geo-lift, and holdouts


## FAQ

**If the data loss is biased, can I just apply a correction factor per channel instead of moving to server-side collection?**

No. The missingness rate varies by channel, by device, and by conversion lag at the same time, and you cannot observe the channel mix of the users you never captured. A flat or even per-channel multiplier assumes you already know the distribution you are missing. That distribution is exactly what a biased sample hides from you.

**Does server-side attribution let me ignore cookie consent or iOS ATT?**

No. Server-side recovers technical losses like Safari's cookie caps, ad-block, and dropped tags. It does not override a legal consent choice. In GDPR jurisdictions a refusing user's data still needs a lawful basis to be processed for tracking. Any vendor pitching server-side as a consent bypass is selling you a liability.

**Is fixing the data the same as saying multi-touch attribution now measures incrementality?**

No. Server-side gives your model a representative sample instead of a skewed one. The model is still correlational. It tells you which touchpoints occurred, not which ones caused the conversion. For real spend decisions, treat MTA as one lens and triangulate with marketing mix modeling, geo-lift, and holdout tests.

**How much will my channel rankings actually change, a 2% tweak or a reordering?**

It depends on your Safari and iOS share, how many of your channels are ad-block-heavy, and your conversion lag. For a Safari-majority, long-consideration, paid-search-heavy funnel, the 24-hour cap alone can move paid versus direct by several points and flip a channel's rank. For short-lag, Chrome-heavy Android funnels, the effect is smaller. Measure your own mix before assuming either.

**My totals already roughly match my backend revenue, so isn't my attribution fine?**

Matching totals is the trap, not the reassurance. Two channel splits can sum to the same revenue while ranking channels completely differently. Incomplete hides inside a correct total. Biased is about the shape of the split, and a total cannot reveal shape.

**Which users am I actually under-counting with client-side tracking?**

The three big groups overlap into roughly the same person: affluent iOS and Safari users hit by Intelligent Tracking Prevention cookie caps, technical and higher-income users running ad blockers, and privacy-conscious users who reject a fair cookie banner. An affluent iOS user running a content blocker who declines your banner is dropped three separate ways.


