Docs · UTM · Monitoring
Build a lightweight audit loop: baseline export, weekly anomaly checks, redirect tests, and fixes for the failures that quietly turn paid and email traffic into Direct.
UTM failures don’t throw errors. A redirect that drops the query string, someone typing
Email instead of email, an affiliate inventing a campaign name —
you notice weeks later when a report doesn’t add up and the raw data can’t be rewritten.
Treat tagging like production: scheduled checks that catch drift in days, plus known fixes per failure mode. Naming and channel rules live in how to optimize UTM tags; Google Ads placement (template vs Final URL suffix) is in the Ideal tracking-template setup.
You can’t spot drift until you know what “correct” looks like today.
In GA4, explore Session source / medium / campaign / manual ad content with Sessions, last 12 months, export. BigQuery is cleaner if you have it:
SELECT
collected_traffic_source.manual_source AS source,
collected_traffic_source.manual_medium AS medium,
collected_traffic_source.manual_campaign_name AS campaign,
COUNT(*) AS events
FROM `project.analytics_XXXXXX.events_*`
WHERE _TABLE_SUFFIX BETWEEN '20250101' AND '20260101'
GROUP BY 1, 2, 3
ORDER BY events DESC
Valid, casing variant of valid, typo, unknown, or empty. Rank by volume — fix high-session defects first; ignore rare one-offs until the top of the list is clean.
Sum Unassigned channel sessions plus (direct) / (none) where you expected a campaign.
That’s the number that justifies the fix work.
Track Unassigned as a % of sessions weekly. Healthy accounts sit near zero. A sustained rise usually means a new campaign shipped with a medium GA4 can’t classify. Treat Unassigned > 1% of sessions as an incident.
Highest leverage. Each week, list source / medium / campaign combos that appeared for the first time in the last 7 days. Approve real launches; catch typos while the campaign is still live.
Automate with a scheduled BigQuery compare against a registry of approved values and mail anything unmatched.
Pure defects — no legitimate reason for mixed case or padded values:
SELECT source, medium, campaign, COUNT(*) AS c
FROM base
WHERE LOWER(medium) != medium
OR LOWER(source) != source
OR TRIM(campaign) != campaign
OR campaign LIKE '%20%'
GROUP BY 1, 2, 3
ORDER BY c DESC
Compare observed utm_medium to your controlled vocabulary.
This is what catches paidsearch, newsletter, Social,
banner, cpc-remarketing and every near-miss people invent.
Flag sessions with utm_source but empty utm_medium, or paid traffic
missing utm_campaign. Partial tags look fine at a glance and poison channel reports.
Tags can be perfect in the ad platform and still die on your site. Weekly cron: request each standard landing URL with known test params, follow redirects, assert the final URL still has them.
curl -sIL "https://example.com/landing?utm_source=test&utm_medium=test&utm_campaign=test" \
| grep -i "^location:"
Inspect the final location for parameter survival. This is the check almost nobody runs — and it catches the most expensive silent failure.
Generate known-good links in the Campaign URL builder or the tracking template builder so the registry and the live URL stay in sync.
Casing splits one campaign into several.
Fix forward in the link builder (lowercase everywhere). You can’t rewrite GA4’s collected dimensions —
consolidate in BigQuery with LOWER(), Looker Studio calculated fields, or custom channel
groups that match case-insensitively.
Wrong channel.
Check utm_medium against GA4’s rules, correct the tag, and use a custom channel group
as a bridge while corrected traffic rolls in.
Redirects strip parameters.
Trailing-slash normalisation, HTTP→HTTPS drops, locale redirects, CDN rules.
Preserve the query string server-side ($query_string in nginx, QSA in Apache),
then re-run link validation.
Untagged channels. Inventory paid search, paid social, organic social, email, affiliates, PDFs, QR codes, signatures, push, partner listings. Each untagged surface inflates Direct or Referral.
Internal links with UTMs.
Crawl your own hrefs for utm_. Every hit restarts the session and steals
acquisition credit — remove them; use GA4 events for on-site promos.
Malformed URLs.
Double ?, double &, missing =, blind concat onto URLs
that already had a query string. Builders beat hand-typed strings.
Alerts say something changed. A registry says whether it was intended. One row per live link: full URL, each parameter, owner, channel, launch date, purpose. New-value detection becomes trivial — anything in analytics not in the registry is either unlogged or wrong.
Not the raw dimensions. Compensate in reporting (custom channel groups, Looker Studio, BigQuery). Prevention is the real fix.
Usually redirects stripping the query string, or a malformed landing URL. Request the tagged link and follow redirects.
Under 1% of sessions. Sustained above that means active tagging problems.
Crawl with any SEO crawler; search extracted internal links for utm_. Fix every hit.
No — a monthly GA4 distinct source/medium/campaign export covers most value. BigQuery just makes the weekly checks cheap to automate.
Live link validation. It catches the failure careful tagging can’t prevent: perfect tags destroyed by your own redirects.