The short answer. WordPress conversion tracking means capturing every form submission, call click and WooCommerce order and delivering it to your ad platforms. Most guides stop at firing a pixel on a thank-you page. That breaks with AJAX forms, page caching and ad blockers. A reliable setup captures the click ID on landing and sends the conversion from the server.
Search this term and the results fall into three buckets: a vendor page for a call-tracking product, Google’s own documentation for the Site Kit plugin, and a long tail of “paste this snippet into your theme” tutorials. All of them answer the same narrow question, which is how to get a tag onto a WordPress page. None of them ask why the tag so often fails to fire, or fires and never reaches the ad account.
If you run Google Ads or Meta campaigns to a WordPress site, that second question is the one that costs money. So this guide starts with where WordPress conversions go missing, then covers the setup that survives.
First, decide which conversion you are tracking
“WordPress conversion tracking” covers three different jobs, and each fails in its own way.
| Conversion type | Typical WordPress source | Where it usually breaks |
|---|---|---|
| Form lead | Contact Form 7, Gravity Forms, WPForms, Elementor Forms | AJAX submit means no page load, so a thank-you-page tag never fires |
| Online order | WooCommerce | Payment redirects, cached thank-you pages, double fires on reload |
| Call or click | Click-to-call links, booking widgets, embedded schedulers | Happens in the browser or on a third-party domain your tag cannot see |
If your site sells products, the purchase case has its own failure modes and its own guide: see WooCommerce conversion tracking. The rest of this post focuses on the table’s first row, because most WordPress sites are lead-gen sites, and lead forms are where the standard advice is weakest.
Where WordPress conversions actually go missing
1. The form never leaves the page. Most modern form plugins submit in the background and swap in a confirmation message: Contact Form 7, Elementor Forms and new WPForms forms all do by default. There is no redirect and no new URL. The classic method, a conversion tag on /thank-you/, relies on a page view that never happens. The plugins that do redirect let you point at a thank-you page, but then anyone who lands on that URL from a bookmark, an email or a crawler counts as a lead.
2. Page caching serves the same page to everyone. Caching plugins and host-level caches store rendered HTML. A thank-you page that prints a per-visitor value (an order ID, a submission ID) can be served from cache with someone else’s value, or with none. Caches also hold a page with the tag in place long after you edited it, so a fix you made on Tuesday is not live on Friday and nothing tells you.
3. Consent and optimization plugins reorder scripts. Consent banners, script minifiers and “delay JavaScript until interaction” settings are common on WordPress and they all move or defer your tags. A conversion tag deferred until the first scroll or click will not fire for the visitor who submits a form and leaves.
4. The click ID is gone by the time the conversion happens. A Google Ads click lands on your page with a gclid in the URL. A Meta click lands with fbclid. If the only thing that reads it is a script, the identifier lives in a script-set cookie. Safari can expire that cookie within a day of an ad click, Firefox’s Strict mode strips known tracking parameters from the URL, and an ad blocker can stop the script from writing anything at all. A lead who fills out the form three days later converts with no click ID attached, so the platform cannot connect the conversion to the ad. The ad blocker conversion tracking guide covers how much traffic this affects.
5. The tag is installed but the request is blocked. Even when the tag fires, the request to the ad platform’s domain is the one blockers and privacy browsers target. Browser-side tracking is the weakest link in the chain, and a WordPress site gets no special protection from it.
None of these show up as an error. They show up as a gap between the leads in your form plugin’s entries list and the conversions in your ad account, and that gap is invisible unless you go looking for it.
The check that takes ten minutes
Before changing anything, measure your own gap.
- Export a month of form entries (or WooCommerce orders) from WordPress, filtered to paid traffic if you can identify it.
- Pull the same month of conversions from Google Ads or Meta for the matching conversion action.
- Compare the counts.
Some difference is normal because platforms attribute differently and not every lead comes from an ad. What matters is the trend and the size. If paid-channel leads in WordPress outnumber conversions in the ad platform by a wide margin, the capture layer is losing them, and the ad platform is optimizing on a partial picture.
The three layers of a setup that holds up
Think of WordPress tracking as three separate layers, each one doing one job.
Layer 1: capture the click ID on landing. A site-wide script reads gclid, gbraid, wbraid, fbclid and msclkid from the landing URL and stores them so they are still available when the conversion happens, whether that is the same minute or three weeks later. If you do nothing else, do this. It is the layer most guides never mention. For the Google side, the Google Ads conversion tracking guide explains how the click ID is used when a conversion is uploaded.
Layer 2: detect the conversion on the server. WordPress is self-hosted PHP, so it has something a SaaS storefront does not: server-side hooks that fire when a form is processed, regardless of whether the visitor’s browser cooperates. On BigCommerce, for contrast, you cannot run your own server code on the platform, which is why BigCommerce conversion tracking needs a different approach. On WordPress the hook is yours to use. The well-known ones:
| Plugin | Hook that fires after a successful submission |
|---|---|
| Contact Form 7 | wpcf7_mail_sent |
| Gravity Forms | gform_after_submission |
| WPForms | wpforms_process_complete |
| WooCommerce | woocommerce_payment_complete for card and gateway payments (see the WooCommerce guide) |
A server hook fires once per submission, with the submitted data in hand, whether the form used AJAX, a redirect, or neither. It cannot be blocked by a browser extension, and a caching plugin has no say in it.
Two catches before you trust one. Gravity Forms runs gform_after_submission for entries marked as spam too, so check the entry before you count it. Contact Form 7 fires wpcf7_mail_sent in demo mode, so a test form still counts, and skips it when the notification email fails to send. If your host’s mail is unreliable, hook wpcf7_submit instead and check the result’s status.
Layer 3: deliver the conversion to the ad platform from the server. Detection is not delivery. The captured conversion still has to reach Google Ads or Meta with the click ID and hashed customer data attached, through each platform’s server-side API. This is the part of the setup that server-side tracking exists for, and it is what turns “we saw the lead” into “the ad platform learned from the lead”.
A minimal server-side example
Here is the shape of Layer 2 using Contact Form 7. It runs on the server after a successful submission and posts a lead event to Convultra’s tracking endpoint, in the format the server-side install guide documents. Any service that accepts server events works the same way: change the URL, the key header and the field names.
<?php
// In a small plugin or your child theme's functions.php.
// Define CONVULTRA_PROJECT_KEY (your proj_ key) in wp-config.php.
add_action( 'wpcf7_mail_sent', function ( $contact_form ) {
$submission = WPCF7_Submission::get_instance();
if ( ! $submission ) {
return;
}
$data = $submission->get_posted_data();
// 'your-email' is the email field's name in Contact Form 7's default form.
$email = isset( $data['your-email'] ) ? strtolower( trim( $data['your-email'] ) ) : '';
$email_hash = $email ? hash( 'sha256', $email ) : '';
// Guard: one event per submission, even if the hook fires twice.
$event_id = 'cf7-' . $contact_form->id() . '-' . $submission->get_meta( 'timestamp' ) . '-' . substr( $email_hash, 0, 12 );
if ( get_transient( 'cv_sent_' . $event_id ) ) {
return;
}
set_transient( 'cv_sent_' . $event_id, 1, DAY_IN_SECONDS );
$event = array(
'project_id' => CONVULTRA_PROJECT_KEY,
'event_id' => $event_id,
'event_type' => 'lead',
);
// The visitor ID cookie the Convultra script set on landing. Convultra stored
// the ad click against it, so this is what joins the lead to the click.
if ( ! empty( $_COOKIE['_cvl_vid'] ) ) {
$event['visitor_id'] = sanitize_text_field( wp_unslash( $_COOKIE['_cvl_vid'] ) );
}
if ( $email_hash ) {
$event['user_data'] = array( 'email' => $email_hash );
}
wp_remote_post( 'https://tracking.convultra.com/v1/track', array(
'timeout' => 2, // a slow tracking call must not stall the form
'headers' => array(
'Content-Type' => 'application/json',
'X-Convultra-Key' => CONVULTRA_PROJECT_KEY,
),
'body' => wp_json_encode( $event ),
) );
} );
The visitor ID is the line that connects Layer 1 to Layer 3. The form submission reaches your server with the visitor’s cookies, so the hook can read the ID the landing script stored and pass it on, and Convultra uses it to look up the click it saved for that visitor. Leave it out and the lead can only be matched on the hashed email.
Three more details carry weight. The guard stops a retried hook from double-counting a lead, and the same event_id lets the receiving end deduplicate a resend. The two-second timeout caps how long a slow tracking call can hold up the visitor’s confirmation message. Setting blocking to false helps less than it sounds: with the cURL transport most hosts use, WordPress still waits for the request to finish. And the email is normalized (trimmed, lowercased) before hashing, because a hash of Jane@Example.com and a hash of jane@example.com are different values to a matching system.
What this does not fix
Server-side capture is not magic, and it is better to know the limits.
- It cannot capture a click ID that was never saved. If your site-wide script is blocked or deferred on the landing page, the server hook still fires but has no click ID to send. That is why Layer 1 comes first and why it should be loaded early, not delayed.
- It does not replace consent. Sending hashed email to an ad platform is still processing personal data. Respect your visitors’ consent choices on the server path exactly as you do on the browser path.
- It does not repair bad conversion definitions. If every form, including the newsletter box and the support form, counts as a lead, the ad platform will optimize toward the cheapest form fill. Separate your conversion actions and value them differently.
- Call and click conversions need their own capture. A click-to-call link is a browser event. Track it with a click handler, and treat call quality as a separate problem.
Where Convultra fits
Convultra takes Layers 1 and 3 off your hands on WordPress, and Layer 2 as well for WooCommerce. Install the Convultra WordPress plugin from your dashboard and paste your project key, or paste the tracking script into your site’s header. The script captures click IDs at first touch with a 90-day retention window and persists them server-side, without a tagging server for you to run. With WooCommerce active, the plugin sends each purchase from your server when payment is confirmed.
For lead forms, the plugin’s Form & Lead Tracking settings map Contact Form 7 and WPForms forms to lead events without touching form code. Those events fire in the browser on submit. If you would rather detect a lead on the server, post it from the form plugin’s hook as in the example above, and leave that form out of the browser rules so it is not counted twice.
To be straight about delivery: automatic forwarding today is Google Ads and OpenAI Ads, with Meta, Microsoft Ads and TikTok coming soon. The server-side tracking page covers how capture and delivery fit together.
A short checklist
- Decide which conversions matter: form lead, order, call. Give each its own conversion action.
- Measure your gap: WordPress entries or orders against ad platform conversions for the same month.
- Install a site-wide click ID capture early in the page, and exempt it from “delay JavaScript” settings.
- Fire conversions from the server hook for your form or checkout plugin, not from a thank-you page view.
- Add a once-per-submission guard before connecting a live ad account.
- Normalize, then hash, email and phone before sending.
- Re-run the gap check in two weeks and compare.
FAQ
How do I track conversions on a WordPress site?
Decide which conversion you are tracking (form lead, order or call), capture the ad click ID on landing, detect the conversion with a server-side hook from your form or checkout plugin, and send it to the ad platform through its server API. Firing a tag on a thank-you page is the simplest method but the most fragile.
Why is my WordPress conversion tag not firing?
The most common causes are AJAX forms that never load a thank-you page, caching plugins serving a stale page, and script-delay or consent plugins deferring the tag until the visitor interacts. Ad blockers can also stop the request after the tag fires.
Can I track Contact Form 7 or Gravity Forms submissions as conversions?
Yes. Contact Form 7 exposes the wpcf7_mail_sent action and Gravity Forms exposes gform_after_submission. Both run on the server after a successful submission, so they work whether or not the form uses AJAX or a redirect.
Do I need Google Site Kit for conversion tracking?
No. Site Kit adds the Google tag to a WordPress site, and its plugin conversion tracking records events from supported plugins such as Contact Form 7, WPForms and WooCommerce. It does that with JavaScript in the browser, so it still depends on the browser. If you want conversions to survive ad blockers and cookie limits, add server-side capture and delivery.
Does server-side conversion tracking work on WordPress?
Yes, and WordPress is well suited to it because it is self-hosted PHP with server-side hooks for forms and WooCommerce orders. You still need to capture the click ID on landing so the server event can be matched to the ad.
Retire the thank-you page
The thank-you page method is a 2015 answer to a 2026 stack. Convultra installs on WordPress with the plugin or one script in your header, and captures the click ID before the form is ever filled in. Start your free trial.