A user clicks your Google Ad with UTM parameters (utm_source=google, utm_campaign=brand_search, utm_term=salesforce_alternative). They land on your homepage. They browse 4 pages over 12 minutes. They eventually fill out the demo request form on a different page. The form captures their name, email, company — everything you asked for. What it does NOT capture: any of the UTM parameters from their original ad click. The campaign that drove the conversion is invisible. The CRM receives the lead labeled "Direct" or "Unknown" source. Marketing budget decisions get made on broken attribution.
This is the standard failure mode for B2B marketing analytics. UTM parameters live in URL parameters; URL parameters only exist on the immediate landing page; they vanish the moment the visitor clicks any internal link to another page. Without explicit preservation infrastructure, multi-page browsing destroys UTM attribution. The fix is hidden form fields populated via JavaScript that read UTMs from a cookie or local storage on form submission — capturing the original ad attribution through any length of browsing journey.
This guide is the hidden form field UTM capture framework we deploy for Dallas B2B clients. The technical implementation via Google Tag Manager (with code examples), the cookie storage strategy that preserves UTMs through multi-day return visits, the first-touch vs last-touch capture patterns, the CRM field mapping that flows attribution to opportunities and revenue, and the case study of a Rowlett-based B2B SaaS company whose UTM recovery audit revealed 67% of their leads had been mis-attributed for 18 months.
UTM parameters die at internal navigation without preservation infrastructure. The problem: URL parameters exist only on landing page; multi-page browsing destroys attribution. The fix — 4 components: (1) JavaScript on every page reads UTM parameters from URL on load, (2) First-party cookie storage persists UTMs through entire session (90-day expiration typical), (3) Hidden form fields auto-populate from cookie at form rendering, (4) CRM integration stores UTMs on lead record and propagates through opportunity/deal lifecycle. First-touch vs last-touch: store BOTH separately; first-touch is immutable, last-touch updates on each visit. Common failure: UTMs captured only on first form submission, then overwritten by subsequent touches.
How UTMs Die: The Standard Failure Mode
Three observations about the attribution problem:
Observation 1: URL parameters don’t persist across pages
UTM parameters (utm_source, utm_medium, utm_campaign, utm_term, utm_content) live in the URL query string. They exist on the landing page only. The moment the user clicks any internal link, the new page’s URL doesn’t carry the UTMs forward. Unless explicit preservation code intercepts and propagates them, they’re gone the moment the user navigates internally.
Observation 2: Forms by default only capture what’s on the current page
If the form is on the same page as the UTMs (single-page landing pages with form in the hero), capture is automatic — the form reads URL parameters or hidden fields populated from URL. For B2B sites where users browse multiple pages before filling forms (case studies → pricing → demo request), the form is rarely on the original landing page. By the time the form is rendered, UTMs are gone from the URL.
Observation 3: Most analytics tools attribute incorrectly without help
Google Analytics 4 has improved UTM attribution but still struggles with multi-session journeys. HubSpot’s built-in source tracking attributes "first touch" but can be confused by single-page apps and dynamic forms. Salesforce out-of-the-box has NO UTM capture — it’s entirely up to your integration to feed UTMs in. The default behavior of most tools is "lose UTM attribution silently."
Run a simple test: visit your own site with UTM parameters (e.g., yourwebsite.com/?utm_source=test&utm_campaign=audit). Browse 3-4 internal pages. Fill out a form. Check the lead that arrives in your CRM — did UTM fields populate? Repeat the test with various entry points. Most Dallas B2B sites we audit capture UTMs on 20–40% of submissions — the ones where the landing page coincidentally has the form. The other 60–80% of submissions arrive with empty UTM fields. That’s your attribution gap.
The Implementation Checklist: 6 Hidden Field Capture Steps
Step 1: GTM Custom HTML tag (fires on all pages)
JavaScript that runs on every page load. Reads URL parameters, stores them in cookie if present.
<script>
(function() {
var utmKeys = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'];
var params = new URLSearchParams(window.location.search);
var captured = {};
utmKeys.forEach(function(k) {
if (params.has(k)) captured[k] = params.get(k);
});
if (Object.keys(captured).length === 0) return;
// Always update last-touch
var expiry = new Date();
expiry.setTime(expiry.getTime() + (90 * 24 * 60 * 60 * 1000));
utmKeys.forEach(function(k) {
if (captured[k]) {
document.cookie = 'last_' + k + '=' + encodeURIComponent(captured[k]) +
';expires=' + expiry.toUTCString() + ';path=/;SameSite=Lax';
}
});
// First-touch: only set if cookie doesn't exist yet
utmKeys.forEach(function(k) {
if (!document.cookie.match(new RegExp('first_' + k + '=')) && captured[k]) {
// First-touch expiration: 365 days (longer to capture original source)
var firstExpiry = new Date();
firstExpiry.setTime(firstExpiry.getTime() + (365 * 24 * 60 * 60 * 1000));
document.cookie = 'first_' + k + '=' + encodeURIComponent(captured[k]) +
';expires=' + firstExpiry.toUTCString() + ';path=/;SameSite=Lax';
}
});
})();
</script>
Step 2: Hidden form field population
On form rendering (or before submission), populate hidden fields from cookies. Add to GTM or directly in your form HTML.
<script>
function getCookie(name) {
var m = document.cookie.match(new RegExp('(^|;)\s*' + name + '=([^;]+)'));
return m ? decodeURIComponent(m[2]) : '';
}
function populateUTMFields() {
['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content'].forEach(function(k) {
var firstField = document.querySelectorAll('input[name="first_' + k + '"]');
var lastField = document.querySelectorAll('input[name="last_' + k + '"]');
firstField.forEach(function(f) { f.value = getCookie('first_' + k); });
lastField.forEach(function(f) { f.value = getCookie('last_' + k); });
});
}
if (document.readyState === 'complete') populateUTMFields();
else window.addEventListener('load', populateUTMFields);
// Also re-populate on form interactions for SPA scenarios
document.addEventListener('focusin', function(e) {
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') populateUTMFields();
});
</script>
Step 3: Hidden field HTML in your forms
Add these to every form on your site. Hidden fields don’t affect user UX; they sit invisibly and submit with the rest of form data.
<!-- First-touch UTMs (immutable original source) -->
<input type="hidden" name="first_utm_source" value="">
<input type="hidden" name="first_utm_medium" value="">
<input type="hidden" name="first_utm_campaign" value="">
<input type="hidden" name="first_utm_term" value="">
<input type="hidden" name="first_utm_content" value="">
<!-- Last-touch UTMs (most recent touch) -->
<input type="hidden" name="last_utm_source" value="">
<input type="hidden" name="last_utm_medium" value="">
<input type="hidden" name="last_utm_campaign" value="">
<input type="hidden" name="last_utm_term" value="">
<input type="hidden" name="last_utm_content" value="">
<!-- Also capture GCLID/fbclid for ad platform attribution -->
<input type="hidden" name="gclid" value="">
<input type="hidden" name="fbclid" value="">
Step 4: CRM custom fields
Create matching custom fields in your CRM. HubSpot example:
- First Touch UTM Source (single-line text, contact property)
- First Touch UTM Medium
- First Touch UTM Campaign
- First Touch UTM Term
- First Touch UTM Content
- Last Touch UTM Source
- Last Touch UTM Medium
- Last Touch UTM Campaign
- Last Touch UTM Term
- Last Touch UTM Content
For Salesforce: same fields on Lead, Contact, and Opportunity objects. Critical: ensure field mapping when Lead converts to Contact/Opportunity (Salesforce default lead conversion can drop custom fields).
Step 5: First-touch immutability rule
First-touch fields should be SET-ONCE. After initial capture, subsequent visits should not overwrite. Implementation:
- Client-side: JavaScript checks if first_utm_source cookie exists; only sets if absent
- CRM-side: field workflow rule prevents updates to first_touch fields once populated
- Marketing automation: HubSpot/Marketo can enforce "set once, then read-only"
The most common implementation mistake: first-touch fields getting overwritten by subsequent touches, destroying long-term attribution.
Step 6: Lifecycle propagation
UTMs must propagate from Lead → Contact → Opportunity → Closed Won. Ensure:
- Salesforce: lead conversion mapping includes both first-touch and last-touch UTM fields
- HubSpot: deal records inherit UTMs from associated contact
- Reporting: closed-won deals show their UTM attribution for ROI analysis
iOS Safari has aggressive privacy features (Intelligent Tracking Prevention) that delete some cookies after 7 days. For users who visit your site on iPhones via Safari, UTM persistence may break after a week. Mitigation: (1) Set cookies as SameSite=Lax (already in script above), (2) Use first-party domains for cookie storage (which we do), (3) For B2B with long cycles, also rely on email-based attribution as backup — once you have the user’s email, you can correlate later visits via login/identification. Covered in detail with Enhanced Conversions in GCLID CRM tracking.
Implementation by Form Platform
HubSpot Forms
- Native UTM tracking enabled in form settings
- BUT: HubSpot captures UTMs only from landing page; needs supplemental script for multi-page journeys
- Use HubSpot’s "tracking code" + supplemental GTM script for full coverage
- UTMs auto-map to HubSpot’s built-in tracking fields (no manual CRM mapping needed)
Marketo Forms
- Hidden field implementation via JS
- Field mapping to Marketo program fields
- Custom Activity Type for "Form Submitted with UTM" enables deeper attribution reporting
Salesforce Web-to-Lead
- Manual setup required (no native UTM capture)
- Add hidden fields to web-to-lead form HTML
- Map to Salesforce custom Lead fields
- Critical: lead conversion mapping for Contact/Opportunity propagation
WordPress / Custom Forms
- Most flexibility but most setup required
- GTM scripts handle population universally
- Form plugin (Gravity Forms, Contact Form 7, WPForms) usually supports hidden fields
- Server-side webhook to CRM with UTM fields
Real Case: Rowlett B2B SaaS Recovers 18 Months of Attribution
In December 2025 we worked with a Rowlett-based B2B SaaS company (workflow software for distribution + logistics, ACV $24K–$120K, ~$11M ARR). Their attribution had been broken for 18 months without them realizing it:
- ~340 demo requests/month
- HubSpot lead source data showed: 67% "Direct," 18% "Organic Search," 8% "Paid Search," 4% "Referral," 3% "Social"
- The 67% "Direct" was suspicious — very few B2B SaaS prospects type URLs directly
- Marketing team had been making budget decisions assuming most leads came direct (so brand awareness wasn’t needed)
- Sales team consistently mentioned "they found us through [campaign description]" but data didn’t support it
- Discrepancy had persisted for 18 months
Implementation across 5 weeks:
- Week 1: Audit. Ran 50 test submissions with various UTM combinations. Discovered: UTMs captured correctly only when form was on the immediate landing page (rare). 80% of form submissions on internal pages arrived with no UTM data — HubSpot defaulted to "Direct."
- Week 2: Built GTM UTM capture script + cookie persistence (90-day window, 365-day for first-touch).
- Week 3: Added hidden fields to all 14 HubSpot forms across the site. First-touch and last-touch UTMs separately captured.
- Week 4: Created HubSpot custom properties for first-touch UTM fields. Configured set-once enforcement rule. Mapped lifecycle propagation through to deal records.
- Week 5: Historical backfill attempt. Used HubSpot’s page-view history + GA4 source data to backfill source attribution for prior leads where possible. Recovered partial attribution for 60% of past 18 months of leads.
Implementation Checklist
- Audit current UTM capture rate — test submissions; see how many arrive with UTMs in CRM.
- GTM script on all pages — reads UTMs from URL, stores in first-party cookies.
- Both first-touch and last-touch fields — first-touch immutable, last-touch updates per visit.
- Hidden form fields on every form — populated from cookies at render time.
- CRM custom fields created — Lead, Contact, Opportunity objects.
- Lifecycle propagation tested — UTMs flow from Lead to Opportunity to closed-won.
- Salesforce lead conversion mapping — UTMs survive lead-to-opportunity transition.
- Historical backfill if possible — recover attribution for past leads using log data.
5 Common UTM Capture Mistakes
- 1. Only capturing UTMs on landing page. Misses 60-80% of multi-page-journey form fills. Cookie persistence required.
- 2. Only single first-touch OR last-touch. Need both. Different reporting needs different views.
- 3. First-touch overwriting on subsequent visits. Destroys long-term attribution. Set-once enforcement essential.
- 4. Salesforce lead conversion drops UTMs. Custom fields not in conversion mapping. Explicit mapping required.
- 5. Not auditing capture rate. Broken attribution invisible without explicit testing. Test quarterly.
For Dallas B2B companies, proper UTM capture infrastructure typically reveals 30–70% of leads were previously mis-attributed — with proportional impact on marketing budget decisions. The investment is small (2–4 weeks of GTM + CRM setup). Pair with the GCLID tracking in GCLID CRM integration and the HubSpot-Salesforce integration in CRM integration audit for complete attribution architecture.
Frequently Asked Questions
What if my forms use a third-party platform (Calendly, Typeform, Chili Piper) that I can’t add hidden fields to?
Most modern booking/form tools support hidden fields. Calendly: "Customize" → custom questions → can pass UTMs via URL parameters. Typeform: hidden fields configurable. Chili Piper: native UTM support in Chili Piper Concierge. For tools without hidden field support: pass UTMs via prefilled URL parameters (e.g., calendly.com/yourname?utm_source=cookie_value). GTM script can dynamically rewrite booking links on your page to include the user’s stored UTMs. Slightly more complex but achievable for any tool that supports URL prefill.
How should I handle UTMs across subdomain transitions (e.g., www → app)?
Two scenarios. (1) Same-organization subdomains (yourcompany.com → app.yourcompany.com): set cookies with domain=".yourcompany.com" so they’re accessible across subdomains. (2) Cross-domain (e.g., yourwebsite.com → yourbookingtool.com): cookies don’t cross. Solution: pass UTMs via URL parameters when linking to cross-domain destinations. GTM can rewrite outbound links to include stored UTMs. For very common cross-domain flows (e.g., always sending demo requests to a third-party tool), build the URL parameter injection systematically rather than per-link.
What about GDPR / privacy compliance with UTM cookies?
UTM cookies are typically classified as "marketing/analytics" cookies under GDPR. Requirements: (1) Cookie consent banner explicitly mentions marketing cookie category and gets opt-in for EU traffic. (2) If user rejects, don’t set UTM cookies (or use anonymized session-only storage). (3) UTMs themselves are usually not considered "personal data" under GDPR — they describe the marketing campaign, not the person. (4) But when associated with a user’s identifiable form submission, they become part of the personal data record — subject to deletion on user request. Most CMP tools (OneTrust, Cookiebot) handle the consent layer transparently.
My CRM only has limited custom fields available — is there a way to consolidate?
Several strategies. (1) Concatenate: instead of 5 separate fields per touch, use one field with format "source|medium|campaign|term|content". Less granular reporting but uses one field instead of five. (2) Use a JSON field if your CRM supports it (HubSpot custom objects, Salesforce text area). (3) Only capture the 2-3 most important UTMs (source, medium, campaign) and skip term/content. For most Dallas B2B clients, capturing source + medium + campaign is sufficient for budget decisions; term/content add detail but rarely change decisions.
How do I attribute closed-won deals back to UTMs months after the original click?
Two requirements. (1) UTMs preserved on lead at original form fill (this article’s focus). (2) UTMs propagate through CRM lifecycle to deal record (covered in HubSpot Salesforce integration). For deal closed 4 months after original lead: deal record shows original UTM attribution from when lead was first captured. Reporting: filter closed-won deals by First Touch UTM Source — see which channels actually produce revenue 30/60/90/180 days post-click. This is the single most valuable B2B marketing report most teams don’t have access to until UTM capture is properly implemented.
Want us to audit your UTM capture?
We’ll test your current capture rate, identify the gaps, build GTM scripts + hidden field infrastructure, configure CRM lifecycle propagation, and measure attribution recovery. Free for B2B companies with 100+ monthly form submissions.
Get a UTM Capture Audit Explore CRO Services