How to capture UTM parameters in forms and keep them
Analytics can tell you which campaign brought a visit. It usually cannot tell you which campaign brought the person who later became your best customer, because that person lives in your form responses and your CRM, not in your analytics. Capturing UTM parameters in the form itself closes that gap, if you handle the two places where UTMs quietly go missing.
By Raj Kumar, Founder, Zunoform

The short answer
Add one hidden field per parameter, named exactly utm_source, utm_medium, utm_campaign (plus utm_term and utm_content if you use them). When someone opens a form link carrying those parameters, the form reads them from the URL and saves them with the response, invisibly. If visitors land on another page first or the form is embedded, store the UTMs on arrival and pass them to the form yourself.
- Hidden field names must match the URL parameter names exactly, and values are case sensitive.
- UTMs only exist on the landing URL; they vanish when the visitor clicks to another page.
- Decide whether you want first-touch, last-touch or both, and name the fields accordingly.
- Never put email addresses or other personal data in UTM values.
How do UTM parameters work?
UTM parameters are tags you add to the end of a link: ?utm_source=newsletter&utm_medium=email&utm_campaign=october-launch. The page they point to ignores them. Analytics tools such as Google Analytics read them when the page loads and attribute that session to the campaign you described.
Google's documentation lists nine parameters and says you should always use utm_source, utm_medium and utm_campaign. Values are case sensitive, so utm_source=Google and utm_source=google are two different sources in your reports. Our UTM builder at /tools/utm-builder assembles tagged links and handles URLs that already have a query string.
Which UTM parameters should your form capture?
Parameters from Google Analytics' URL builder documentation, checked 28 September 2026.
| Parameter | Identifies | Example | Capture in the form? |
|---|---|---|---|
| utm_source | The referrer | newsletter, linkedin, google | Always |
| utm_medium | The channel type | email, social, cpc | Always |
| utm_campaign | The campaign or promotion | october-launch | Always |
| utm_content | Which link or creative | header-button, video-ad-b | If you compare creatives |
| utm_term | Paid search keyword | form-builder | Paid search only |
| utm_id | Campaign ID | abc.123 | If you import cost data |
| utm_source_platform | The buying platform | Search Ads 360 | Rarely |
| utm_creative_format / utm_marketing_tactic | Creative type / targeting | video / remarketing | No: Google says GA4 does not currently report them |
Sources: Google Analytics Help: Collect campaign data with custom URLs
What is a hidden field, and why use it for UTMs?
A hidden field is a form field the respondent never sees. Instead of being typed, its value is filled in from the page URL (or a default value) and stored alongside the visible answers. Named utm_source, it picks up whatever utm_source the link carried.
That puts the source on the lead record itself. To take a made-up example, analytics can report that the October email drove 40 submissions; only the form response can tell you that the one lead who became a large account came from that email. It also keeps working for visitors whose browsers block analytics scripts, because the tagged URL still reaches the form. It does record where someone came from, so mention it in your privacy notice.
How do you capture UTM parameters in a form? (step by step)
The general method works in most builders; the notes in brackets describe Zunoform.
Decide what you need
Usually utm_source, utm_medium and utm_campaign. Add utm_content if you compare buttons or creatives, and first-touch fields if the sales cycle is long (see below).
Add the hidden fields
Create one hidden field per parameter with the exact lowercase name. (In Zunoform: Settings, turn on Hidden fields, add each name, optionally with a default value such as "direct" for visits with no tag.)
Build tagged links
Tag the form link itself for emails, ads and social posts, using consistent lowercase values. Keep a shared list of allowed sources and mediums so two people don't invent "LinkedIn" and "linkedin-ads".
Test with a real submission
Open the tagged link in a private window, submit, and check the response. (In Zunoform the values appear in the response detail, as extra columns in the form's CSV export and in a connected Google Sheet, and in webhook or Zapier payloads.)
Handle landing pages and embeds
If the tagged link points to a landing page rather than the form, the parameters must be carried over. The next two sections cover this, and it is where most setups break.
Report by source
Group responses by utm_source and utm_campaign in your export or CRM, and compare lead quality, not just counts.
Why do UTM parameters go missing before the form?
UTMs live in the URL of the first page someone lands on. Three common situations drop them before the form ever sees them.
The visitor browses first
An ad lands on your pricing page with ?utm_campaign=spring. The visitor clicks to Features, then to Contact. The Contact page URL has no parameters, so a hidden field on that form reads nothing. This is the most common reason UTM hidden fields come back empty.
The form is embedded in an iframe
An embedded form runs at its own URL inside a frame. Unless the embed code copies the host page's query string onto the frame's address, the form sees none of it. Check what your builder's embed code does. Zunoform's embed script copies the host page's query string onto the frame, so a visitor who lands on the page that holds the form with ?utm_source=google is captured without extra work (add data-forward-params="false" to switch that off, or data-params to set values of your own). A plain iframe snippet copies nothing, so there you add the parameters to the iframe's src yourself, as in the snippet below.
Redirects and link shorteners
Most shorteners and redirects keep the query string, but some site redirects (http to https, trailing slashes, a language switch) rebuild the URL without it. Click your tagged link and look at the address bar where you end up.
First-touch vs last-touch: which should you store?
The fix for browsing visitors is to save the UTMs on arrival. First you have to decide which arrival counts.
| Model | What it records | Best for |
|---|---|---|
| First touch | The campaign that brought the person the very first time, kept even if they return later via another link | Measuring which channels find new people; long B2B cycles |
| Last touch | The most recent tagged visit before they converted | Measuring which campaign closed the deal; short cycles |
| Both | Two sets of fields, e.g. ft_source and utm_source | Most teams: costs two extra hidden fields and settles arguments |
How do you keep UTM parameters across pages?
A small script on your site saves the parameters when a visitor lands and adds them to the form link or iframe later. Where you save them changes how long they last.
| Storage | How long it lasts | Scope | Watch out for |
|---|---|---|---|
| sessionStorage | Until the tab or window closes | One origin, one tab | Lost if they come back tomorrow; fine for same-visit attribution |
| localStorage | No expiry by design | One origin (protocol + domain) | Safari deletes script-writable storage after 7 days of browser use without interaction on your site |
| First-party cookie | Whatever expiry you set | Can span subdomains | Same Safari 7-day cap for script-set cookies; count it in your cookie consent |
| Server-side (CRM or tag manager) | As long as you keep it | Your systems | More setup; usually needs a developer |
None of these works across different devices. A visitor who clicks an ad on a phone and converts on a laptop arrives untagged.
Sources: MDN: Window.localStorage · WebKit: Full Third-Party Cookie Blocking and More (7-day storage cap)
A copyable snippet: store first- and last-touch UTMs and pass them to your form
<script>
(function () {
var KEYS = ["utm_source", "utm_medium", "utm_campaign", "utm_term", "utm_content"];
var params = new URLSearchParams(window.location.search);
var now = {};
KEYS.forEach(function (k) { var v = params.get(k); if (v) now[k] = v.toLowerCase(); });
var first = {}, last = now;
try {
if (Object.keys(now).length) {
if (!localStorage.getItem("ft_utms")) localStorage.setItem("ft_utms", JSON.stringify(now));
localStorage.setItem("lt_utms", JSON.stringify(now));
}
first = JSON.parse(localStorage.getItem("ft_utms") || "{}");
last = JSON.parse(localStorage.getItem("lt_utms") || "{}");
} catch (e) { /* storage blocked: fall back to this page's URL */ }
// Adds last-touch as utm_* and first-touch as ft_* to form links and iframes.
document.querySelectorAll('a[href*="/form/"], iframe[src*="/form/"]').forEach(function (el) {
var attr = el.tagName === "IFRAME" ? "src" : "href";
var url = new URL(el.getAttribute(attr), window.location.href);
Object.keys(last).forEach(function (k) { if (!url.searchParams.has(k)) url.searchParams.set(k, last[k]); });
Object.keys(first).forEach(function (k) { url.searchParams.set("ft_" + k.slice(4), first[k]); });
el.setAttribute(attr, url.toString());
});
})();
</script>An example to adapt, not an official Zunoform script. Place it before the closing body tag, after your form links or a plain iframe whose src is your form URL (a script-injected embed may not exist yet when it runs; with Zunoform's embed script, write the stored values into the element's data-params attribute before embed.js loads instead). Add hidden fields named utm_source, utm_medium, utm_campaign, utm_term, utm_content, ft_source, ft_medium, ft_campaign, ft_term and ft_content, and keep only the ones you use.
How do you send UTM data to your CRM or Google Sheets?
Once the UTMs are on the response, they should travel with it. In most builders hidden fields are included wherever answers go: the spreadsheet row, the webhook payload, the CRM contact. Map utm_source to your CRM's lead source property and utm_campaign to a campaign property, rather than dumping them into a notes field nobody filters on.
Be precise about what your tool actually sends. In Zunoform, hidden fields appear in the response view, the per-form CSV export and the Business plan's REST API, and they travel with each delivery: Google Sheets and Excel rows get one extra column per hidden field (after the answer, location and user-agent columns), webhook, Zapier and workflow payloads carry them in a hidden object (hidden.utm_source), and Airtable fills columns with the same name. HubSpot and Mailchimp do not receive them yet, so to get UTMs into those, route the lead through Zapier or a webhook.
However you move the data, clean the values on the way in: lowercase them and map known variants ("fb", "facebook.com") to one name, or your source report will have six rows for Facebook.
How do you track form conversions by source in GA4?
GA4 already attributes each session to its UTM source, so what it needs from you is a reliable conversion event. Its enhanced measurement can send form_start and form_submit automatically for forms on your own pages, though Google notes the parameters need custom dimensions before they show in reports. The recommended event for a lead is generate_lead, which Google describes as firing when a user submits a form; mark it as a key event.
Embedded forms are the catch. GA's tag on your page cannot see clicks inside an iframe served from another domain, so form_submit will not fire for it. The dependable pattern is to redirect respondents after submitting to a thank-you page on your own site and fire generate_lead there. Zunoform can redirect after submit, and the redirect URL can carry answers, such as the plan someone picked, so the thank-you page can send them as event parameters. Test this on an embedded form: its redirect loads inside the frame, where browsers may limit analytics cookies, so it is most reliable when the form is opened as a direct link.
Keep personal data out of that URL. Google's guidance is that URL paths and parameters sent to Analytics must be free of PII, so pass a plan name or budget band, never an email or phone number.
Source: Google Analytics Help: Enhanced measurement events
Source: Google Analytics Help: Recommended events (generate_lead)
Source: Google Analytics Help: Best practices to avoid sending PII
UTM hidden field mistakes to check for
- Hidden field names match the URL parameters exactly, including underscores and lowercase.
- Tagged links point at the page that holds the form, or a script carries the UTMs over.
- Embedded forms receive the parameters on the iframe URL, verified with a real test submission.
- Internal links on your own site are not tagged; that overwrites the real source with your own.
- A default value such as "direct" separates untagged visits from broken capture.
- No personal data in any UTM value or redirect URL that analytics will see.
- Anonymous surveys do not use hidden fields that could identify someone, such as an email passed in a mail-merge link.
Forms where source tracking pays off
Contact details, company size, goals, and buying timeline for inbound leads.
Role, team size, and focus areas so the demo lands on what matters.
Project scope, quantities, timeline, and an optional budget branch.
Session choice, role, and audience questions for live online events.
A gated-content form: email, role, and company size for the download.
Pre-launch email capture with use case and urgency for prioritizing invites.
Where Zunoform fits (and where it doesn't)
Hidden fields are on every Zunoform plan, including Free: you name the parameters, set optional defaults, and the values are saved with each response, shown in the response detail and exported as CSV columns. Redirect after submit is on every plan too, and redirect URLs can carry answers to a thank-you page. If you switch a form to anonymous mode with hidden fields on, the builder warns you that URL values can still identify people.
Hidden field values go out with every Google Sheets, Excel, Zapier and webhook delivery, and the embed script forwards your page's query string to the form. The limits are real, though. The native HubSpot and Mailchimp integrations do not map hidden fields to contact properties, and there is no built-in first-touch storage, so the snippet above is your job. If you need UTMs written straight onto CRM contact properties without a Zapier or webhook step, a tool whose native CRM integration maps hidden fields, or a dedicated attribution plugin, is a better fit today.
Questions people ask
How do I capture UTM parameters in a form?
Add hidden fields named exactly like the parameters (utm_source, utm_medium, utm_campaign) and send people to the form with a tagged link. The form reads the values from its URL and saves them with each response. If visitors land on another page first, or the form is embedded, store the UTMs on arrival and append them to the form URL.
What is a hidden field in a form?
A hidden field is a field the respondent never sees. Its value comes from the page URL, a default, or a script, and it is saved alongside the visible answers. Common uses are UTM parameters, a customer or order ID from a personalized link, or the page the form was embedded on.
Why are my UTM hidden fields empty?
Usually because the form's own URL never had the parameters. Visitors browsed from the tagged landing page to another page, the form is in an iframe that does not receive the host page's query string, or a redirect stripped it. Test by opening a tagged link in a private window and submitting.
Should I store first-touch or last-touch UTMs?
Store both if you can. First touch shows which channels introduce new people, last touch shows which campaign preceded the conversion, and the difference is often the most useful part of the report. It costs a second set of hidden fields and a few lines of script that saves the first set without overwriting it.
Are UTM parameters case sensitive?
Yes. Google Analytics treats utm_source=Google and utm_source=google as different sources, and most CRMs do the same with text values. Agree on lowercase values, keep a shared list of allowed sources and mediums, and lowercase values when you capture them.
Do UTM parameters survive an embedded form?
Only if the embed passes them on. An embedded form loads at its own address inside a frame, so it sees the frame's URL, not your page's. Some embed codes copy the page's query string automatically, as Zunoform's embed script does; others, and any plain iframe, do not, so you add the parameters to the iframe URL yourself.
Can I send UTM data to Google Sheets or a CRM?
In most builders, yes: hidden fields travel with the answers to integrations. Check yours with a test submission. In Zunoform, hidden fields are added as columns in Google Sheets and Excel and sent in Zapier and webhook payloads, as well as appearing in the response view, the CSV export and the Business REST API. The native HubSpot and Mailchimp integrations do not carry them.
Raj Kumar · Founder, Zunoform
Raj Kumar is the founder of Zunoform, the form builder made by Symphonic Grow. He builds the product and writes these guides, checking every competitor price and feature claim against the vendor's own pages before publishing.
Raj Kumar on LinkedInKeep reading
Form design
Google Forms
Integration setup
Buyer's guide
Survey methods

Ready when you are
Better forms.
Better data.
Start with a conversation, a document or a single page.
Unlimited forms and 500 responses per month on Free.
No card required.
- No credit card
- Unlimited forms
- 500 responses / month