Site icon Haktan Suren, PhD

Contact Form 7 UTM Tracking: How to Pass UTM Parameters With Every Lead

Illustration of five UTM values entering hidden Contact Form 7 fields and flowing to email, stored submissions, and a CRM contact record.

A Contact Form 7 submission can arrive with the name, email, and message perfectly intact while every campaign field is blank.

That is not a small reporting problem. It means the lead survived, but the reason the lead arrived did not.

When I need to pass UTM parameters through Contact Form 7, I treat it as a four-part handoff: capture the values, put them into real CF7 hidden fields, add the matching mail tags, and verify the final destination.

If any one of those steps is missing, the form can still show a success message. That is why this problem stays hidden.

The short version

What you seeMost likely breakWhat I check first
Hidden input is empty before submitCapture script, form-tag setup, caching, or script orderInspect the rendered input value in the browser
Input has a value, but email is blankMail tag is missing or does not match the form field nameCompare the Form and Mail tabs character for character
Email shows a shortcode or form tag literallyWordPress shortcode syntax and CF7 form-tag syntax were mixedReplace the legacy wrapper with a current CF7 hidden tag
Email has the value, but CRM is blankWebhook, Zapier, or CRM field mappingInspect the outgoing payload and destination mapping
Tagged landing page works sometimesCache or loss between landing and conversion pagesTest a cached page and an untagged second page separately

The important distinction is between a form tag and a mail tag.

The form tag creates the field that receives the value. The mail tag tells Contact Form 7 where to place the submitted value in the email template.

You usually need both.

Method 1: capture UTM parameters from the current URL

Contact Form 7 has a native hidden form tag. Its current source registers the hidden tag and renders it as a real HTML hidden input. The same source supports a default:get option that reads a query-string value with the same field name.

For a form that appears on the tagged landing page, the exact form-tag pattern is:

[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]

A visitor opening this URL:

https://www.example.com/contact/?utm_source=google&utm_medium=cpc&utm_campaign=summer_demo

should get a rendered input whose name is utm_source and whose value is google.

This method is simple. It is also narrow.

It reads the current request. If the visitor lands on one page, browses to an untagged contact page, and submits there, the contact page no longer has the original UTMs. A hidden field cannot remember a value it never received.

It is also sensitive to full-page caching because the hidden value is created when WordPress renders the form. If a cache ignores query strings and serves the same HTML to everyone, the form can receive an old or empty value.

How a CF7 hidden field becomes a mail value

The full path is worth spelling out because the square brackets can make the setup look more magical than it is.

  1. Contact Form 7 reads [hidden utm_source default:get] in the Form tab.
  2. On a request containing utm_source=google, CF7 renders an HTML input named utm_source with the value google.
  3. The browser includes that hidden input when the visitor submits the form.
  4. Contact Form 7 receives utm_source as posted form data.
  5. The mail tag inserts that posted value into the Mail template.

The mail tag does not go back to the landing URL and look for UTMs. It reads the submitted field. This is why adding to the Mail tab without adding a form field does not create tracking.

The same boundary applies to every destination after Contact Form 7. Each step receives a named value from the step before it.

LayerNative exampleHandL example
CF7 form field nameutm_sourceutm_source_cf7
Rendered HTML input nameutm_sourceutm_source_cf7
CF7 mail tag[utm_source_cf7]
Webhook or Zapier inpututm_sourceutm_source_cf7
CRM destinationWhatever CRM field you mapWhatever CRM field you map

I do not rename fields casually after a form is live. A rename in the Form tab can break the Mail tab, stored-message expectations, Zapier samples, webhook payloads, and CRM mappings at the same time.

Method 2: capture once and populate the CF7 fields later

For a normal lead journey, I prefer to capture the attribution on arrival and keep it until the visitor converts.

That is the role of a persistence layer. It reads the landing values, stores them according to the site’s consent setup, and populates the form when Contact Form 7 is ready.

HandL UTM Grabber is one way to do this on WordPress. Its current Contact Form 7 integration guide uses these field tags:

[hidden utm_source_cf7 utm_source_cf7-{formid} class:utm_source id:utm_source]
[hidden utm_medium_cf7 utm_medium_cf7-{formid} class:utm_medium id:utm_medium]
[hidden utm_campaign_cf7 utm_campaign_cf7-{formid} class:utm_campaign id:utm_campaign]
[hidden utm_term_cf7 utm_term_cf7-{formid} class:utm_term id:utm_term]
[hidden utm_content_cf7 utm_content_cf7-{formid} class:utm_content id:utm_content]

The current integration also provides a UTM Fields (HandL) tag generator that inserts the fields for you. I would use that instead of copying the wrapper syntax from the 2018 version of this article.

The field name matters. In this setup, the submitted source field is utm_source_cf7, not utm_source. The Mail tab, webhook, Zapier step, and CRM mapping must use the name that your form actually submits.

This is where a “tracking” problem can really be a naming problem.

Add the matching mail tags

After adding hidden fields under the Form tab, open the Mail tab and include the matching mail tags in the message body.

For the native fields from Method 1:

Campaign source: 
Campaign medium: 
Campaign name: 
Campaign term: 
Campaign content: 

For the HandL fields from Method 2:

Campaign source: [utm_source_cf7]
Campaign medium: [utm_medium_cf7]
Campaign name: [utm_campaign_cf7]
Campaign term: [utm_term_cf7]
Campaign content: [utm_content_cf7]

Contact Form 7 matches the mail tag to the submitted form field by name. If the Form tab says utm_campaign_cf7 and the Mail tab says utm_campaign, those are two different fields.

Where the value goes after submit

DestinationWhat receives the valueWhat you must configure
Email notificationThe CF7 mail tag in the Mail templateAdd the mail tag that matches the hidden form field
FlamingoThe submitted field in the stored inbound messageInstall and activate Flamingo; confirm the field appears in the saved message
CRM webhook or connectorThe outgoing payload fieldMap the submitted CF7 field name to the correct CRM property
ZapierThe field exposed by the Contact Form 7 trigger or connectorRefresh the trigger sample, then map that field in the next Zap step

Contact Form 7 does not store submitted messages by itself. The official Flamingo plugin exists for that job and stores the submission data in the WordPress database.

A webhook or CRM handoff is separate again. Contact Form 7’s default plugin description says it does not send data to external servers by itself. An integration has to do that work.

If you are using Zapier, keep the handoff as a separate step. My Zapier for Contact Form 7 walkthrough covers that downstream path. I would not merge it into this setup because the two pages answer different questions: this one gets the value into the submission; the Zapier article moves it onward.

Why Contact Form 7 UTM tracking breaks

1. The hidden field renders empty

Inspect the field before submitting. For the native setup, run this in the browser console:

document.querySelector('input[name="utm_source"]')?.value

For the HandL setup, use utm_source_cf7 instead.

If the result is empty, the failure happened before the mail or CRM step. Check whether the URL contains the value, whether the form tag is correct, and whether the capture script runs after the form field exists.

This script-order problem appears with forms loaded in a popup, modal, or delayed page section. Custom JavaScript can run successfully and still find nothing because Contact Form 7 has not rendered the input yet.

2. The value arrives as a literal shortcode string

Contact Form 7 form tags and WordPress shortcodes are not interchangeable.

If an email contains something like [utm_source_i] or the page prints the wrapper instead of a hidden input, the syntax was inserted into a context that no longer evaluates it. Replace the legacy wrapper with a current CF7 hidden tag or the current HandL tag generator output.

3. The email has the value, but the CRM does not

This is good news, in a limited way. The browser capture and Contact Form 7 submission worked.

Now inspect the outgoing webhook or Zapier sample. Confirm that the payload contains the field, then compare its name with the destination CRM property. My broader blank UTM field diagnosis follows the same boundary: find the last place where the value is present, then fix the next handoff.

Caching is the first infrastructure layer I check

A Contact Form 7 page can look normal while its hidden attribution fields are stale.

The native default:get method is evaluated while the server renders the form. If a page cache does not vary by the query string, it can reuse HTML that was created without the current visitor’s UTMs.

A persistent tracking setup has a different risk. The form HTML may be fine, but an aggressive cache or CDN can ignore the query args or cookies that the attribution layer needs.

I do not start by replacing Contact Form 7. I check the cache behavior. My WordPress hosting and UTM tracking guide shows the layers I inspect: host cache, CDN, Varnish, WordPress cache plugin, query-string rules, and cookie exclusions.

How I verify UTM parameters in Contact Form 7

  1. Open a tagged URL with unmistakable values: ?utm_source=cf7_qa&utm_medium=manual&utm_campaign=contact_form_test_202608.
  2. Inspect the hidden input in the browser and confirm its value before submitting.
  3. Submit with a unique email address so the test is easy to find.
  4. Open the email notification and confirm source, medium, and campaign separately.
  5. If Flamingo is active, open the saved inbound message and confirm the same fields.
  6. If a webhook or Zapier sends the lead onward, inspect the run data before checking the CRM.
  7. Open the CRM record and confirm the mapped destination fields.
  8. If persistence is expected, repeat the test after navigating to an untagged page.

I also test one direct or untagged visit. A good setup should not invent campaign values when no campaign data exists.

If you are choosing a form plugin rather than fixing an existing Contact Form 7 setup, my WordPress form plugin lead-tracking audit compares that broader decision. This article assumes you already use CF7 and want the handoff to work.

What I would not over-claim

My final checklist

The form is not finished when it says “sent.” It is finished when the same campaign value reaches every place that depends on it.

Exit mobile version