Say your GoHighLevel CRM shows 40 new leads this week and Google Ads shows 18. Nobody is lying. Google Ads just never saw the rest.
To track GoHighLevel form leads with Google Ads enhanced conversions, you catch the form's submit message with a small listener in Google Tag Manager, push the lead's email and phone into the data layer, and hand that data to your Google Ads conversion tag. Google hashes it with SHA-256 and uses it to match the lead to the ad click.
This matters because Smart Bidding learns from the leads it can see. Miss half of them and it bids on weak signals, so your cost per lead creeps up while the real lead count stays flat. I find this in audits all the time, and it is the daily work I do as a GoHighLevel tracking specialist. Below is the five-step version of the fix.
Watch GoHighLevel form leads Tracking
Step 1: See why GoHighLevel form leads go missing in Google Ads
Most GoHighLevel forms sit on your page inside an iframe. Think of an iframe as a glass window in a brick wall. Your landing page is the wall and the form is a separate room behind the glass. Your tag manager can tell that something happened, but it cannot read what the visitor typed.
- Browsers stop your page from reading inside a form that lives on another domain. This is called the same-origin rule.
- So the normal GTM form submit trigger sees nothing, and a form field variable comes back empty.
- A thank-you page redirect can still count the lead, but it carries no email or phone to match.
Enhanced conversions for leads is the part that sends hashed email and phone with each lead. Google compares them with signed-in Google accounts to find the click that started the journey. Without it you depend on cookies alone, and cookies get lost.
Do this:
- Open your landing page and check how the GoHighLevel form is added: an iframe embed or a link to a hosted page.
- Submit one real test lead and note what you see in GoHighLevel. Check Google Ads later, because conversions can take a few hours to show.
- Write down the gap. That is your starting point.
Step 2: Catch the form signal with a listener in Google Tag Manager
A GoHighLevel form sends a message out of the iframe when someone submits it. Your page can listen for that message. It is called a postMessage listener, and it works because the message is built to cross the glass.
I add the listener as a Custom HTML tag that fires on Initialization, so it is ready before the form loads. When a message arrives, it checks that it is a form submit, reads the email, phone and name, and pushes an event called ghl_lead_submitted into the data layer. The push carries these keys:
- event: ghl_lead_submitted
- enhanced_user_data.email and enhanced_user_data.phone_number
- enhanced_user_data.first_name and enhanced_user_data.last_name
- ghl_form_id, so you can tell your forms apart
Do not trust field names blindly. GoHighLevel changes its forms from time to time, so log the message once in the browser console and match your keys to what you see. If you have not built the basic GoHighLevel conversion yet, start with my basic GoHighLevel conversion setup guide.
Do this:
- In GTM, create a Custom HTML tag with a clear name, such as chtml GoHighLevel form listener.
- Add the listener code that watches for the message event coming from the form.
- Set the trigger to Initialization, All Pages.
- Open Preview, submit a test lead and check that ghl_lead_submitted appears in the left panel.
Step 3: Turn the lead data into GTM variables and a trigger
Now GTM has to read what the listener pushed. Make one data layer variable for each key: email, phone number, first name, last name and the form ID. Then build one User-Provided Data variable that maps those fields in one place.
You do not have to hash anything yourself in this setup. The Google tag cleans the values and hashes them with SHA-256 before they leave the browser. If you want to see what hashing does to an email, try my free SHA-256 hashing tool.
Send clean values. Phone numbers should carry the country code and emails should be real addresses with no spaces. Weak data gives weak matching, and I would rather send three clean fields than ten messy ones.
Do this:
- Create data layer variables for email, phone_number, first_name, last_name and ghl_form_id, using the enhanced_user_data path.
- Create a User-Provided Data variable, choose manual configuration and map each field to its variable.
- Create a Custom Event trigger for ghl_lead_submitted.
- Not sure how your data layer should look? Build the structure with my free dataLayer code generator.
Step 4: Turn on enhanced conversions and set up the Google Ads tag
Start in Google Ads. Open your lead conversion action, find the enhanced conversions settings, tick the box and choose Google Tag Manager as the method. If you do not have a lead conversion action yet, create one first.
Then go to GTM and open your Google Ads Conversion Tracking tag with the conversion ID and label. Tick the option to include user-provided data from your website, pick the variable from Step 3 and fire the tag on the ghl_lead_submitted trigger.
In the video I also map extra fields such as state, city and postal code, for forms that collect them. More matching fields usually means a better match, but only send what you collected with permission.
Do this:
- In Google Ads, open the lead conversion action and turn on enhanced conversions with Google Tag Manager.
- In GTM, open the Google Ads Conversion Tracking tag and tick the box to include user-provided data.
- Select your User-Provided Data variable and set the trigger to ghl_lead_submitted.
- Map email, phone, first name and last name first. Add address fields only if your form collects them.
- Prefer to have it built and tested for you? Here is my done-for-you GoHighLevel tracking setup.
Step 5: Test it, fix the usual errors and protect the data
Use Preview mode and one real test lead. Check three things: the ghl_lead_submitted event appears, the Google Ads tag fires, and the user-provided data shows as sent with real values. Then open the diagnostics of your conversion action in Google Ads, which can take a day or two to update.
- The event never fires: check that the form's iframe has no sandbox rule that blocks messages.
- Email or phone is undefined: GoHighLevel may have renamed a field. Read the message in the console and fix the data layer path.
- Status says Needs attention: the User-Provided Data variable is missing or the format is wrong. Add the country code to phone numbers.
Two more things. First, consent: if you serve visitors in regions that need it, collect consent first and pass the signals along. Test your banner with my Consent Mode checker. Second, ad blockers and Safari can still drop browser requests, so you can send the same conversion from a server container on Stape. For that route, read my server-side GoHighLevel tracking guide.
Do this:
- Run a test lead in Preview mode and confirm the event, the tag and the sent data.
- Check the conversion diagnostics in Google Ads after a day or two.
- Add Consent Mode v2 if your visitors need to give consent.
- When the basics are stable, add the server-side route for extra reliability.
Quick answers
What are enhanced conversions for leads in Google Ads?
A feature that sends hashed first-party data, such as email and phone, from your lead form to Google. Google uses it to match the lead to an ad click when cookies alone fall short.
Why can't Google Tag Manager read my GoHighLevel form fields?
Most GoHighLevel forms load in an iframe from another domain. Browsers stop your page from reading inside it, so you need a message listener instead of a normal form trigger.
Do I need to hash the email and phone myself?
In a standard Google Tag Manager setup, no. The Google tag cleans the values and hashes them with SHA-256 before sending. You pass clean values and it handles the rest.
Does this work without server-side tracking?
Yes. The web container version works on its own. A server container adds resilience against ad blockers and Safari limits, but it is an upgrade, not a requirement.
Key takeaways
- GoHighLevel forms usually live in an iframe, so GTM cannot read the fields with a normal form trigger.
- A small postMessage listener catches the submit message and pushes the lead's email and phone into the data layer.
- One User-Provided Data variable and one custom event trigger connect that data to your Google Ads conversion tag.
- Turn on enhanced conversions in Google Ads and tick the user-provided data option in the tag.
- Test in Preview mode with a real lead, then check the conversion diagnostics a day or two later.
- Add Consent Mode v2, and a server container on Stape if you want more resilience.
Summary
To track GoHighLevel form leads with Google Ads enhanced conversions, use a Google Tag Manager listener to catch the form's submit message, push the lead's email and phone into the data layer, and pass them through a User-Provided Data variable to the Google Ads conversion tag. This guide is for agencies and lead-gen businesses whose CRM shows more leads than Google Ads. Turn on enhanced conversions in Google Ads, test in Preview mode, fix the common errors, and add Consent Mode v2 and optional server-side tracking.
Want the full written guide with the code? It is here: https://mdniamul.com/blog/track-gohighlevel-form-leads-google-ads-enhanced-conversions/
Does your GoHighLevel CRM show more leads than Google Ads right now, and by how much?
MD Niamul
Conversion Tracking and Web Analytics Specialist, Digital Soldier Agency





Top comments (3)
If you build the listener, log event.data once in the console before you write the data layer push. Field names are the first thing that breaks.
Small gotcha: check the origin of the message in your listener, so only the form's domain can push data into your data layer.
Curious how others debug iframe forms. I mostly rely on Tag Assistant preview plus the console.