A WooCommerce store can have every Meta Pixel event firing and still get weak results from Meta ads, because the events arrive without enough information for Meta to know who performed them. Event Match Quality, the score Meta shows per event in Events Manager, measures exactly that. Low scores mean lost attribution, smaller custom audiences and worse optimisation.
This guide shows how I implement Meta Pixel through Google Tag Manager on WordPress and WooCommerce with manual Advanced Matching, so every event carries the customer data Meta can use.
What Advanced Matching Sends
With Advanced Matching, the pixel sends normalised and hashed values for fields such as:
ememailphphone with country codefnandlnfirst and last namect,st,zp,countrycity, state, postcode and countryexternal_idyour customer ID
Meta hashes them in the browser before sending. The more fields you provide, the higher the match rate.
Step 1: Clean Up Existing Pixels
Search the theme and plugins for existing pixel code. Common culprits: the Facebook for WooCommerce plugin, PixelYourSite, theme options with a pixel ID field, and code pasted into header scripts. Decide on GTM as the single source and disable the rest. Duplicate pixels inflate events and break deduplication.
Step 2: Build the User Data Layer
Push customer data to the data layer wherever you have it. A small plugin or child theme snippet handles the three cases:
add_action('wp_head', function () {
$user = [];
if (is_user_logged_in()) {
$u = wp_get_current_user();
$user = [
'user_email' => strtolower($u->user_email),
'user_first_name' => $u->first_name,
'user_last_name' => $u->last_name,
'user_external_id' => (string) $u->ID,
];
}
if (function_exists('is_order_received_page') && is_order_received_page()) {
$order = wc_get_order(absint(get_query_var('order-received')));
if ($order) {
$user = [
'user_email' => strtolower($order->get_billing_email()),
'user_phone' => preg_replace('/\D+/', '', $order->get_billing_phone()),
'user_first_name' => $order->get_billing_first_name(),
'user_last_name' => $order->get_billing_last_name(),
'user_city' => $order->get_billing_city(),
'user_state' => $order->get_billing_state(),
'user_zip' => $order->get_billing_postcode(),
'user_country' => $order->get_billing_country(),
'user_external_id' => (string) ($order->get_customer_id() ?: ''),
];
}
}
if ($user) {
echo '<script>window.dataLayer=window.dataLayer||[];window.dataLayer.push(' . wp_json_encode(array_filter($user)) . ');</script>';
}
}, 1);
On the checkout page, add a small script that reads the billing fields as the customer types and pushes them to the data layer, so InitiateCheckout and any later events on that page can use them.
Step 3: Ecommerce Data Layer
Use the WooCommerce GA4 guide on this site for view_item, add_to_cart, begin_checkout and purchase. The Meta tags read from the same ecommerce object.
Step 4: GTM Variables
Create Data Layer Variables for each user field: user_email, user_phone, user_first_name, user_last_name, user_city, user_state, user_zip, user_country, user_external_id. Create the ecommerce variables for transaction ID, value, currency and items, plus a Custom JavaScript variable that maps ecommerce.items to an array of item_id values for content_ids.
Step 5: Meta Pixel Tags
Use the official Meta Pixel template from the Community Template Gallery.
Base pixel on All Pages with Advanced Matching enabled and every user field mapped to the corresponding parameter. The template normalises and hashes them. Add an ad_storage consent requirement if you run a consent banner.
ViewContent on view_item with content_ids, content_name, content_type of product, value and currency.
AddToCart on add_to_cart with the same parameters.
InitiateCheckout on begin_checkout with content_ids, num_items, value and currency.
Purchase on purchase with content_ids, content_type, num_items, value, currency and eventID set to the order number for deduplication with the Conversions API.
Each event tag inherits the user data from the base pixel's init call, and the template also lets you pass user data per event. Map it on Purchase as well, because the order received page is where you have the most complete details.
Step 6: Conversions API
Send the same events from a server-side GTM container with the same event IDs. On WooCommerce you can trigger the server purchase from the browser event routed through the server container, or from a woocommerce_payment_complete hook that posts to the server container directly, which catches orders where the customer never saw the thank-you page. Include the hashed user fields and the fbp and fbc cookies.
Step 7: Verify
- GTM Preview. Browse as a guest, add to cart, check out with a test gateway. Confirm each Meta tag fires once with the expected parameters, and that the user fields are populated on checkout and the order received page.
- Meta Pixel Helper should show the Advanced Matching parameters on each event, hashed.
- Events Manager, Test Events: confirm the events arrive with matched parameters listed, and that Purchase shows as deduplicated when the server event arrives.
- Events Manager, Overview: within a few days, Event Match Quality for Purchase should rise into the good range. Scores in the high range typically need email, phone, name, address and the fbp and fbc cookies all present.
Common Problems
- Match quality unchanged. User fields are pushed after the base pixel fired. Make sure the user data layer push runs at priority 1 in
wp_head, before GTM. - Phone not matching. Missing country code. Normalise to digits with the country prefix.
- Purchase duplicates. The order received page reloaded with no event ID, or browser and server IDs differ.
- Consent banner blocks everything. Expected for declined visitors. Confirm the base pixel fires after acceptance by adding the consent update event as a trigger.
Outcome
Meta receives complete ecommerce events with the customer data it needs to match them to people. Attribution improves, custom audiences grow, and the Purchase event becomes a reliable optimisation signal. For most WooCommerce stores the Event Match Quality score moves from poor to good within a week of this setup.
