When more than half your GA4 traffic shows as Direct and you're running paid search, display, and social campaigns, something is wrong. Not with your campaigns. With your consent configuration.
Direct traffic in GA4 is the default bucket. When Google can't determine where a session came from, it goes to Direct. A reasonable amount of Direct is normal. But 50%, 60%, 66% Direct on a site with active paid channels means GA4 is losing attribution signal somewhere. One of the most common causes: consent mode isn't sending the data Google needs to model session sources.
How consent mode attribution works
When a visitor denies consent, GA4's behavior depends on which consent mode you're running.
Advanced mode sends cookieless pings. No cookies are set, no user ID is stored, no session stitching happens. But Google gets a degraded signal: a timestamp, a page URL, an event type. From this, Google's conversion modeling can estimate the likely source of the session using aggregate patterns across consenting and non-consenting visitors.
Basic mode sends nothing. When consent is denied, tags don't fire. Google gets zero signal. The session can't be modeled. It defaults to Direct or Unassigned.
The same thing happens when Advanced mode is technically enabled but misconfigured. If tags are blocked before the consent signal arrives, or if an unnecessary consent check prevents a tag from firing in its degraded mode, the result is the same as Basic: no signal, no modeling, no attribution.
The difference between "some signal" and "no signal" is the difference between knowing where your traffic came from and watching it disappear into Direct.
Three misconfigurations that break this
These come from a recent container audit for a SaaS company running paid search, display, and social campaigns. The container had 56 tags with consent issues across three categories.
Wrong Additional consent type on ad tags
44 advertising tags had analytics_storage configured as an Additional consent hard-block, layered on top of correct Built-In ad consent checks. When a visitor granted ad consent but denied analytics consent, the Additional check blocked the tag anyway. The tag's Built-In consent settings were correct. The Additional layer overrode them.
This is the double-gate problem. A tag with correct Built-In consent for ad_storage, ad_user_data, and ad_personalization still gets blocked because someone added analytics_storage as an Additional requirement. The tag can't fire in its degraded cookieless mode because the Additional check treats denied analytics_storage as a hard stop.
CMP timing at 0ms
The consent initialization had waitForUpdate set to 0ms. This value controls how long GTM waits for the CMP to report the visitor's consent state before firing tags with the default (denied) settings.
At 0ms, GTM doesn't wait at all. Tags fire immediately with all consent types denied. By the time the CMP loads and updates consent, the initial pageview has already been sent as a fully denied hit. Even if the visitor had previously accepted cookies, the first event of every session was sent without that information.
Tags with no consent checks
6 tags had no consent configuration at all. On Google's native templates (GA4, Google Ads), Built-In consent checks are always active when consent mode is running, so this is less of an issue. On Custom HTML tags, no consent configuration means the tag fires unconditionally regardless of the visitor's consent choice. No Built-In protection, no Additional checks, no gate of any kind.
What the data looked like
After fixing all three issues, the channel distribution shifted within one reporting period. Three months of GA4 data, same site, same traffic levels.
| Channel | Before fix | % | After fix | % |
|---|---|---|---|---|
| Direct | 17,223 | 66.6% | 15,614 | 54.8% |
| Organic Search | 2,362 | 9.1% | 4,936 | 17.3% |
| Paid Search | 2,203 | 8.5% | 3,900 | 13.7% |
| Referral | 2,064 | 8.0% | 2,487 | 8.7% |
| Unassigned | 1,943 | 7.5% | 822 | 2.9% |
| Display | 0 | 0.0% | 312 | 1.1% |
| Paid Social | 0 | 0.0% | 177 | 0.6% |
| Organic Social | 54 | 0.2% | 124 | 0.4% |
Direct dropped 11.8 points. Organic Search gained 8.2 points. Those organic sessions were always there. GA4 just couldn't attribute them.
Display and Paid Social went from literally zero reported sessions to 312 and 177 respectively. The ad spend was happening. The traffic was arriving. GA4 couldn't see it because the consent configuration was blocking the signal Google needed to model the source.
Unassigned dropped from 7.5% to 2.9%. That's sessions GA4 previously couldn't classify at all, now modeled correctly because cookieless pings were reaching Google's servers.
The fix was three configuration changes. waitForUpdate from 0ms to 500ms. Remove the unnecessary analytics_storage Additional consent from the 44 ad tags. Add consent settings to the 6 unconfigured tags. No new tags, no custom scripts, no third-party tools.
What to check
Three signals that consent configuration is affecting your attribution:
Direct above 40-50% with active paid campaigns. Some industries naturally have higher Direct traffic (strong brand, lots of bookmarks, email-driven). But if you're spending on paid search, display, and social, and Direct is the dominant channel by a wide margin, consent configuration is worth investigating.
Unassigned above 5%. Unassigned means GA4 received the session data but couldn't classify the channel. A small percentage is normal. Above 5% usually means consent mode isn't sending enough signal for Google to model the source.
Paid channels showing zero sessions when spend is active. This is the clearest signal. If Google Ads shows clicks but GA4 shows zero sessions for that channel, something between the click and the session is breaking. Consent is the most likely cause.
If any of these match, start with your tag consent settings and your CMP's waitForUpdate timing. Those two account for the majority of consent-related attribution loss.
The fix is configuration
The attribution shift above came from three settings changes across a GTM container. No new tags were created. No code was written. No third-party tools were added. The consent infrastructure was already in place. It just wasn't configured correctly.
TagManifest checks tag consent settings as part of a container scan. It flags the tag-level misconfigurations (wrong Additional consent type, missing consent settings) that cause this pattern. It won't catch CMP timing or cookie classification issues because those live outside the container JSON. But the tag layer is where most of the recoverable attribution signal lives.