UTM parameters stripped between the click and the session
GA4 reads campaign parameters from the landing URL at the moment a session starts. If a redirect, a link wrapper, a cross domain hop or a single page app router removes them before that moment, the session has no campaign attached, and the revenue that follows lands in direct or unassigned.
Updated
The symptom: paid traffic landing in direct
Direct traffic is the largest channel in the property, and a lot of it lands on deep pages nobody would type from memory. Sessions in GA4 are well below clicks in the ad platform for the same campaign and the same window. Revenue that you know came from an email send appears under direct. Some sessions land in unassigned. Paid campaigns show impressions and clicks in the ad platform and almost no sessions in analytics.
The tell is the mismatch pattern. If every channel is down by a similar share, you are probably looking at consent or tag loss. If one campaign, one email, or one landing page is down and everything else is normal, you are looking at a link that loses its query string.
Where UTM parameters get stripped
GA4 assigns session source and medium at session start, reading it from the campaign parameters on the landing URL. If those parameters are absent, it falls back to the referrer. If the referrer is empty or is your own domain, the session is direct.
Unassigned is a different failure. It means a source and medium arrived but matched no channel group rule, usually because the medium is a value Google’s definitions do not recognize.
There are five places the parameters usually disappear.
Redirects that rebuild the URL. This is the most common single cause. Any hop that constructs a destination rather than forwarding the original loses everything after the question mark. HTTP to HTTPS, non-www to www, adding or removing a trailing slash, locale detection, an old path mapped to a new one, an edge or CDN rule. Each hop is an opportunity to lose the string, and campaign URLs commonly pass through three or four.
Vanity URLs with server side redirects. A short marketing path is pointed at the real landing page by a redirect rule. Those rules are usually written in a CMS field that accepts a destination path, not a rewrite, so the query string is not carried. Print, QR codes and offline campaigns almost always run on vanity URLs, which is why that whole class of campaign is often invisible.
Email and link wrappers. Email platforms rewrite links to a click tracking domain that then redirects to the destination. Most preserve the query string. Some drop it, some re-encode it, and some append their own parameters in a way that truncates yours. Shorteners add another hop. And parameters placed after a fragment are not query parameters at all, so they are never read as campaign values no matter how correct they look.
Cross domain journeys. A visitor moves from your site to a checkout, booking engine or payment page on another domain. Without cross domain measurement configured, the second domain starts a fresh session with your own site as the referrer, so the campaign is replaced by a referral from yourself. Once configured, GA4 appends a linker parameter to outbound links to the domains you list. Two things still break it: the destination redirects and drops the linker parameter, or enough time passes that the linker’s short validity window expires.
Single page apps that rewrite the URL. A router reads the query on mount, acts on it, and then replaces the address with a clean one. If the first page_view fires after that, the location the tag reports has no campaign on it. The same thing happens when the tag is delayed behind a consent banner or a lazily loaded container and the router wins the race. The signature is a campaign that is visible in the address bar for a fraction of a second and gone by the time you look.
One related case: with ad storage denied, click identifiers are not stored, so a gclid present on the landing page can be absent everywhere after it unless URL passthrough is enabled.
How to trace where parameters drop
Trace the redirect chain from outside the browser. Request the exact URL you put in the ad and follow each hop, reading the destination at every step.
curl -sIL "https://example.com/promo?utm_source=diag&utm_medium=diag&utm_campaign=trace"
The hop where the query string disappears is your answer, and it takes about ten seconds to find.
In the browser, open developer tools, enable preserve log, paste the tagged URL, and read the document requests in order. That catches client side rewrites that a server request will not show.
In GA4 DebugView, read the page location parameter on the first page_view of a session rather than trusting the address bar. In a single page app those two disagree, and the tag’s version is the one that counts.
Compare clicks in the ad platform against sessions by campaign for the same window. A persistent one-sided gap on a single campaign points at a single link.
Examine direct traffic by landing page. Direct sessions arriving on a deep product or offer page are not direct traffic.
For unassigned sessions, inspect the session source and medium. If values are present, your problem is naming, not stripping.
How to preserve the query string
Forward the query string on every redirect. Web server and edge rules can preserve it explicitly. CMS redirect fields frequently cannot, and those rules need to move to the server or the edge.
Remove hops. Tag the final URL directly. Every redirect you delete is an entire class of failure deleted with it.
Keep vanity URLs, but implement them as a rewrite that carries the query, or have the vanity handler append the campaign parameters itself. A vanity URL has a known campaign, so it can add its own.
Configure cross domain measurement for every domain in the journey, then verify that the linker parameter survives the destination’s own redirects rather than assuming the configuration is enough.
In single page apps, capture the entry query string at the earliest point in the document, before the router runs, hold it, and use the captured value for the first page_view. Do the same when the container is loaded behind consent.
Enable URL passthrough so click identifiers survive internal navigation when ad storage is denied.
Standardize naming. Lowercase parameter names, lowercase values, no spaces, and mediums that match GA4’s channel definitions rather than inventions like promo, or a paid campaign tagged medium=social, which lands in organic social. GA4’s paid definitions key off mediums that contain cp, equal ppc or retargeting, or begin with paid. Build every link from one generator so the convention is enforced rather than remembered.
Test one link per campaign per channel before launch. Stripping is usually link specific, not site wide, so a single site level check will not find it.
What misattribution costs you
Take a brand spending $20,000 a month across paid channels and booking 1,000 orders, 300 of which arrive through a landing URL sitting behind a redirect that drops the query string. Those 300 orders are recorded as direct. Paid ROAS reads roughly 30 percent below reality, direct looks like the strongest channel in the business, and the budget conversation that follows moves money away from the campaign that is working.
There is a subtler version that is harder to spot. GA4’s attribution models look past a direct session to earlier non-direct touches inside the lookback window. A session whose campaign was stripped is not always shown as direct: it can be credited to an earlier touch, such as a newsletter from three weeks ago. The channel totals still add up, so nothing looks obviously broken, but the credit sits on the wrong channel and every optimization decision inherits it.
And if those conversions are imported into an ad platform for bidding, the bidding model trains on the misattributed set. Wrong attribution does not stay in the report. It becomes spend.
Campaign parameters are the cheapest attribution mechanism you have and the easiest to lose, because losing them requires nothing more than one redirect written by someone who was not thinking about measurement. The Missing Conversions Audit traces the live link paths for your real campaigns, finds the hop that drops the string, logs every gap it finds, fixes the top 10 in a separate GTM workspace, and hands your developers a precise list of what has to change at the server. $900 flat, 5 business days.
Questions
Why does GA4 show unassigned instead of direct?
Unassigned means the session carried a source and medium that matched no channel group rule. Direct means it carried nothing at all. Unassigned is usually a naming problem, such as a medium that GA4's definitions do not recognize as paid, and it is fixed in your link convention rather than in your redirects.
Does Google Ads auto-tagging avoid this problem?
Not by itself. Auto-tagging appends a click identifier to the landing URL, and that identifier is a query parameter like any other, so the same redirect that strips your campaign parameters strips the click identifier too. Auto-tagging is worth using, but it needs the same redirect trace before you rely on it.
How do we test whether a link keeps its parameters?
Follow the URL through every redirect hop and check whether the query string survives each one, then confirm what the tag actually reported by reading the page location on the first page_view in DebugView. Testing the address bar alone is not enough, because a single page app can rewrite the URL after the tag has already read it.
Our checkout is on another domain. Is that the same problem?
It is a related one. Without cross domain measurement, the second domain starts a new session and attributes it as a referral from your own site, so the campaign is lost even though nothing stripped a parameter. Configure cross domain measurement for every domain in the journey, then verify the linker parameter is still present after the destination's own redirects.
Related auditGA4 audit
Related failures
Next step
Find out what this is costing you.
The Missing Conversions Audit is a fixed-price teardown of your GA4, Google Tag Manager, ad pixels and consent setup. Every gap logged, the top 10 fixed and validated. $900 flat. 5 business days.
A 15 minute call
- We look at your setup from the outside and say what we can already see.
- You get a straight answer on whether the audit is worth it for your account.
- If it fits, we book the slot and send the access checklist.
Opens the calendar here. Nothing loads from Calendly until you do.
Prefer email? hello@missingconversions.com