You’ve tagged your ad URLs with UTM parameters. Clicks are coming in. But when you check your form submissions, the traffic source fields are empty. The UTM data was right there in the URL — so why didn’t it make it into the form?
The answer: most WordPress form plugins don’t capture URL parameters automatically. You need hidden fields configured to pull that data in — and if the visitor navigates to another page before submitting, the UTM parameters are no longer in the URL at all.
This guide walks through exactly how to set up hidden fields for UTM tracking in the seven most popular WordPress form plugins — Contact Form 7, WPForms, Gravity Forms, Fluent Forms, Elementor Pro, Ninja Forms, and Forminator — plus the fundamental problem with all of these approaches and a simpler alternative.
How Hidden Fields Work for UTM Tracking
A hidden field is a form field that exists in the HTML but isn’t visible to the visitor. It can hold a value — like a UTM parameter — and that value gets submitted along with the rest of the form data when the visitor clicks “Submit.”
The basic workflow is:
- Add a hidden field to your form for each UTM parameter you want to capture (utm_source, utm_medium, utm_campaign, utm_term, utm_content).
- Configure each hidden field to pull its value from the corresponding URL query parameter.
- When the visitor submits the form, the UTM values are included in the submission data alongside their name, email, and message.
Every major WordPress form plugin supports hidden fields, but the setup is different for each one. Here’s how to do it in each plugin.
Contact Form 7
Contact Form 7 has built-in support for hidden fields that read directly from URL parameters. No additional plugin is required for basic capture.
Add these lines to your form template in the CF7 editor:
[hidden utm_source default:get]
[hidden utm_medium default:get]
[hidden utm_campaign default:get]
[hidden utm_term default:get]
[hidden utm_content default:get]
The default:get attribute tells CF7 to read the value from the URL query string. If the page URL contains ?utm_source=google, the utm_source hidden field will be populated with “google.”
To include these values in your email notifications, add the corresponding mail tags to your Mail template:
Traffic Source: [utm_source]
Medium: [utm_medium]
Campaign: [utm_campaign]
Term: [utm_term]
Content: [utm_content]
Limitation: The default:get method only reads the URL of the current page. If the visitor landed on Page A with UTM parameters but submits the form on Page B, the hidden fields will be empty.
WPForms
WPForms uses Smart Tags to populate hidden fields from query strings. Note that hidden fields require at least the Basic paid plan — they’re not available in WPForms Lite.
Step 1: In the form builder, drag a Hidden Field from the “Add Fields” panel into your form.
Step 2: Click the hidden field to open its settings. Go to the Advanced tab and find the Default Value box. Click “Show Smart Tags” and select “Query String Variable.”
This inserts {query_var key=""} into the Default Value. Between the empty quotes, type the UTM parameter name:
{query_var key="utm_source"}
Step 3: Repeat for each UTM parameter. Create five hidden fields total, each with its own query_var key: utm_source, utm_medium, utm_campaign, utm_term, utm_content.
Step 4: To include UTM data in your email notifications, go to Settings → Notifications. In the message body, click “Show Smart Tags” and select each hidden field to insert its merge tag.
Limitation: Same as CF7 — query string Smart Tags only read the current page URL. If the form is on a different page from the landing page, the fields will be blank.
Gravity Forms
Gravity Forms has a built-in “dynamic population” feature specifically designed for this use case.
Step 1: Add a Hidden field to your form for each UTM parameter.
Step 2: Click the field, go to the Advanced tab, and check “Allow field to be populated dynamically.”
Step 3: In the Parameter Name box that appears, enter the UTM parameter name exactly: utm_source, utm_medium, utm_campaign, utm_term, or utm_content.
When someone visits a URL like yoursite.com/contact/?utm_source=google&utm_medium=cpc, Gravity Forms reads the query string and populates the matching hidden fields automatically.
Limitation: Dynamic population uses PHP to read the query string server-side. This means it won’t work on cached pages — the server serves the pre-built HTML without running PHP, so the fields remain empty. This is a significant problem for any WordPress site using caching (which should be most of them). It also doesn’t persist UTM data across pages.
Important: If you send Gravity Forms notifications using the {all_fields} merge tag, be aware that hidden fields are included in the output. Use individual field merge tags if you want to control what appears in notification emails.
Fluent Forms
Fluent Forms supports hidden fields with URL parameter population through its shortcode system.
Step 1: In the form builder, go to the Advanced Fields section and drag a Hidden Field into your form.
Step 2: Click the hidden field to open its settings. In the Default Value box, use Fluent Forms’ dynamic shortcode:
{dynamic.utm_source}
This tells Fluent Forms to read the utm_source query parameter from the URL and use it as the field’s default value.
Step 3: Create separate hidden fields for each UTM parameter: {dynamic.utm_medium}, {dynamic.utm_campaign}, {dynamic.utm_term}, {dynamic.utm_content}.
Fluent Forms integrates with Zapier and direct CRM connections (Salesforce, HubSpot, Pipedrive), so you can pass UTM data straight through to your sales tools.
Limitation: The {dynamic.parameter} shortcode reads the current page URL only. No cross-page persistence.
Elementor Pro Forms
Elementor Pro’s form widget supports hidden fields with a dynamic “Request Parameter” tag that reads URL query strings.
Step 1: In the Elementor editor, add a Form widget (or edit an existing one). Add a new field and set its Type to “Hidden.”
Step 2: Label the field (e.g., “utm_source”). Go to the Advanced tab and find the Default Value setting. Click the dynamic tags icon (the stacked-layers icon) and select “Request Parameter.”
Step 3: In the Request Parameter settings, set the Param Name to utm_source. Optionally add a Fallback value (e.g., “direct”) for visitors arriving without UTM parameters.
Step 4: Repeat for each UTM parameter. You should have five hidden fields, each with its own Request Parameter dynamic tag.
You can view the captured data in Elementor → Submissions in your WordPress admin, and it’s included in form notification emails automatically.
Limitation: The Request Parameter dynamic tag only reads the URL of the page where the form is rendered. If the visitor navigated from a landing page with UTMs to a different page with your form, the parameters won’t be captured.
Ninja Forms
Ninja Forms uses merge tags to populate fields from URL query strings.
Step 1: Add a Hidden field to your form from the field panel.
Step 2: In the field settings, find the Default Value box. Click the merge tag selector icon (or type manually) and select the “Query String” merge tag from the “Other” category.
Step 3: Replace YOUR_KEY with the actual UTM parameter name:
{querystring:utm_source}
Step 4: Repeat for each UTM parameter with its own hidden field: {querystring:utm_medium}, {querystring:utm_campaign}, {querystring:utm_term}, {querystring:utm_content}.
The hidden fields will be populated when a visitor arrives on the form page with UTM parameters in the URL. The label you give each field is only visible in the Ninja Forms builder — visitors won’t see it.
Limitation: Query string merge tags read the current page URL only. Same cross-page limitation as every other plugin listed here.
Forminator
Forminator (by WPMU DEV) supports hidden fields with a built-in query parameter option, though it has some quirks.
Step 1: In the form builder, click “Insert Fields” and add a Hidden Field.
Step 2: Click the hidden field to open its settings. Set the Default Value to “Query Parameter” and enter the parameter name (e.g., utm_source).
Step 3: Repeat for each UTM parameter you want to capture.
Important: If UTM values aren’t being captured, enable Ajax rendering in Forminator’s settings. Several users have reported that the query parameter option only works reliably with Ajax form loading enabled.
Technical note: If you try to populate hidden fields via client-side JavaScript (rather than Forminator’s built-in query parameter option), be aware that Forminator performs server-side validation that can reject JavaScript-injected values. For custom JavaScript approaches, you’ll need to use Forminator’s forminator_custom_form_submit_before_set_fields PHP hook to modify data before it’s saved.
Limitation: Same as the others — query parameter values are read from the current page URL only.
The Problem Every Method Above Shares
Every form plugin method described above has the same fundamental limitation: hidden fields can only read UTM parameters from the URL of the page the form is on.
In practice, this means the setup only works if the visitor lands on the exact page that contains your form and submits it without navigating anywhere else. That’s a narrow scenario.
Here’s what actually happens in most real-world sessions:
- Visitor clicks your Google Ad and lands on
/landing-page/?utm_source=google&utm_medium=cpc - They browse to your pricing page:
/pricing/— UTMs are gone from the URL - They click “Contact Us” and land on
/contact/— still no UTMs - They submit your form. Hidden fields read the current URL. No UTM parameters found. The submission records blank traffic source data.
This isn’t an edge case. On most websites, the majority of form submissions happen on a page that’s different from the landing page. A hidden field that reads the current URL will miss them all.
There are also secondary issues:
- Caching breaks server-side population. Gravity Forms’ dynamic population runs in PHP. If your page is served from cache, PHP doesn’t execute, and the fields stay empty.
- Each form plugin requires different configuration. If you use more than one form plugin (say, WPForms for contact forms and Gravity Forms for multi-step applications), you need to configure hidden fields separately in each one. Different syntax, different settings panels, different merge tags.
- Click IDs are ignored. The native hidden field methods above only handle UTM parameters. They don’t capture ad platform click IDs like gclid (Google Ads), fbclid (Meta Ads), msclkid (Microsoft Ads), li_fat_id (LinkedIn), or ttclid (TikTok) — all of which are critical for closing the loop between ad spend and lead generation.
- Maintenance overhead. Every time you add a new form, you need to remember to add the hidden fields, configure the correct merge tags or dynamic population settings, and include the fields in your notification templates. Miss one form and you have a tracking gap.
The Fix: Cookie-Based Persistence
The solution to the cross-page problem is to capture UTM parameters when the visitor first arrives and store them in a browser cookie. The cookie persists as the visitor navigates from page to page, and the UTM data can be read from the cookie on any page — regardless of what the current URL looks like.
You can implement this yourself with custom JavaScript: read the URL parameters on page load, write them to a cookie, then use JavaScript to inject the cookie values into hidden form fields at submission time. It works, but you’ll need to write and maintain the code across WordPress and theme updates, handle each form plugin’s specific JavaScript API, and deal with edge cases like Safari ITP cookie limits.
Or you can let a plugin handle all of it automatically.
Skip the Hidden Fields Entirely
LeadSourcePro takes a different approach. Instead of requiring you to add hidden fields to every form, it hooks directly into your form plugin at the server level and attaches UTM data to each submission automatically.
Here’s what the workflow looks like:
- A lightweight vanilla JavaScript file runs on every page. When a visitor arrives with UTM parameters or ad click IDs in the URL, the script captures them and stores them in a first-party cookie.
- The visitor browses your site freely. The cookie persists across all pages.
- When they submit any form, LeadSourcePro reads the cookie server-side and attaches the traffic source data to the submission — without any hidden fields, shortcodes, or form template edits.
- The submission appears in your WordPress dashboard with the full attribution trail: traffic source label, all five UTM parameters, click IDs, landing page URL, and referrer.
It works automatically with Contact Form 7, WPForms, Gravity Forms, Ninja Forms, Formidable Forms, Fluent Forms, Elementor Pro Forms, Forminator, and Jetpack Forms. For forms that aren’t covered by those nine integrations, there’s a [utm_tracker_fields] shortcode fallback.
Beyond UTM parameters, LeadSourcePro also captures ad platform click IDs — gclid (Google), fbclid (Meta), msclkid (Microsoft), li_fat_id (LinkedIn), and ttclid (TikTok) — and auto-detects the traffic source label. Even visitors arriving without UTM parameters get categorised as Organic Search, Direct, or Referral based on their referrer data.
Because the capture runs in the visitor’s browser via JavaScript, it’s unaffected by server-side caching. And because the data is stored in a cookie with a configurable conversion window (same session, 1 day, or 7 days), it persists across every page on your site.
Hidden Fields vs. Automatic Capture
| Manual Hidden Fields | LeadSourcePro | |
|---|---|---|
| Setup per form | 5+ hidden fields per form, configured individually | None — works automatically |
| Cross-page tracking | No (reads current URL only) | Yes (cookie-based) |
| Works with caching | Client-side methods only; Gravity Forms’ PHP method breaks | Yes (JavaScript-based capture) |
| Click ID capture | Requires additional hidden fields and custom code | gclid, fbclid, msclkid, li_fat_id, ttclid included |
| Traffic source detection | Manual — you interpret raw UTM values | Auto-labelled (Google Ads, Meta Ads, Organic, etc.) |
| New form setup | Must remember to add hidden fields every time | Automatic for all supported plugins |
| Multi-plugin support | Different config for each plugin | One install covers all 9 plugins |
The free version includes all tracking features, all form integrations, dashboard reporting, and CSV export — limited to the 10 most recent entries on a rolling basis. Pro plans unlock unlimited entries, per-submission email notifications, and summary reports.
All data stays in your WordPress database. No external servers, no third-party dependencies. IP addresses are automatically anonymised.
Frequently Asked Questions
Do I still need hidden fields if I use a UTM tracking plugin?
It depends on the plugin. Some UTM tracking plugins (like HandL UTM Grabber and Attributer) still require you to add hidden fields to your forms — they just handle populating them from cookies instead of the URL. LeadSourcePro doesn’t require hidden fields at all for its nine supported form plugins; it hooks into the form submission process directly.
Can I capture gclid and fbclid in hidden fields?
Yes, using the same techniques described above — create a hidden field and set the query parameter name to gclid or fbclid. However, the same cross-page limitation applies: the click ID is only in the landing page URL. If the form is on any other page, the hidden field will be empty unless you implement cookie-based persistence.
Why are my Gravity Forms hidden fields always empty?
The most common cause is page caching. Gravity Forms’ dynamic population feature uses PHP to read query string values, and cached pages skip PHP execution entirely. Either exclude your form pages from caching, switch to a JavaScript-based method for populating the fields, or use a plugin that handles persistence via client-side cookies.
What if I use more than one form plugin on my site?
You’ll need to configure hidden fields separately in each plugin, using that plugin’s specific syntax and settings. This is one of the main arguments for using a dedicated UTM tracking plugin — it provides a single solution across all your forms regardless of which plugin powers them.
Does the hidden field method work on mobile?
Yes, hidden fields work on mobile browsers exactly the same way as desktop. The fields are invisible to the user on any device. However, mobile sessions are more likely to lose UTM data because mobile browsers are more aggressive about cache behaviour and users frequently switch between in-app browsers and native browsers (which drops query parameters).
Stop Losing Lead Source Data
Hidden fields are a step in the right direction — they capture UTM data that would otherwise never make it into your form submissions. But the manual setup is fragile, plugin-specific, and breaks the moment a visitor navigates away from the landing page.
For reliable lead attribution across every page and every form, cookie-based persistence is the way forward. You can build it yourself with custom JavaScript, or install LeadSourcePro and have it working in under two minutes — no hidden fields, no form edits, no code.
See how it works or install the free version and start tracking your next form submission.

Leave a Reply