Webclat / PostHog Practice

Why PostHog's campaign attribution shows "none" or the wrong channel

Attribution breakage almost always traces back to one of three things: the link that sent the visitor, the point where the session's first-touch properties got dropped, or PostHog's own channel-type classification disagreeing with what you expected. None of those are bugs to report - they are things to check, in order.

What PostHog is actually tracking

PostHog captures UTM parameters and referrer data as event and person properties on the first pageview of a session (and, depending on configuration, on the first pageview ever for that person). The web analytics dashboard then buckets sessions into channels using $channel_type - a classification PostHog derives from UTM values, referrer domain, and a set of built-in rules for known ad platforms and search engines.

Attribution "breaking" almost always means one of these properties never got set correctly, not that PostHog failed to record an event.

The diagnostic order that actually works

CheckWhat it catches
1. The actual landing URLUTM parameters stripped by a redirect chain, link shortener, or a CDN/edge rule that rewrites query strings before the page loads
2. Session stitchingA visitor whose session ID changed between the UTM-bearing landing page and the event you are checking - common after a login/identify call that starts a new session under different assumptions
3. Self-referralInternal navigation (your own domain as the referrer) overwriting a real external referrer because of how the session was defined - check PostHog's self-referral handling rules against current docs, since exact defaults have changed between versions
4. $channel_type classificationA source PostHog's built-in rules do not recognize (a newer ad platform, an unusual UTM medium value) falling into a generic bucket instead of the specific channel you expected
5. Client-side blockingThe same ad-blocker/ITP loss that affects any capture - see the reverse proxy guide if this is the actual root cause

Implementation done properly

Fixing attribution for good, rather than one broken link at a time, means: standardizing UTM conventions before campaigns launch (so classification does not depend on guessing at medium values), confirming your marketing platform's outbound links are not being rewritten by a redirect service that drops query strings, and deciding explicitly how cross-domain and self-referral traffic should be treated rather than accepting whatever the default does.

How to verify it worked

  1. Send a test visit through the actual campaign link end to end (ad platform preview or a real click, not a hand-typed URL) and confirm the landing pageview event carries the expected UTM properties.
  2. Check the session in PostHog's session explorer for the correct $channel_type assignment.
  3. Re-pull the web analytics dashboard for that channel after the fix and confirm the volume moved from "none"/miscategorized into the correct bucket - a fix that only looks right in the raw event properties but does not change the dashboard rollup has not actually closed the loop.

Frequently Asked Questions

Why does PostHog show a campaign as "direct" or "none"?

Usually because the UTM parameters never reached the landing pageview event - stripped by a redirect, a link shortener, or an edge rule - rather than a PostHog capture failure. Check the actual landing URL first.

Can I fix historical attribution after the fact?

Not for sessions that already recorded without the property - PostHog attributes at the time of capture. You can fix classification going forward and, in some cases, backfill via the API if you have the source data elsewhere, but that is a data-engineering project, not a settings change.

Get a campaign attribution audit, not another guess.

We trace one broken campaign end to end - link, session, classification - and fix the UTM conventions so the next one does not break the same way.

Audit My Attribution