Tracking performance on Meta (Facebook/Instagram) is increasingly dependent on server-side implementation. Browser-based Pixel tracking alone is no longer reliable due to iOS 17 privacy protections, ad blockers, and limited cookie lifespans.
π― Goals
- Full-funnel event tracking for Meta (PageView, ViewContent, AddToCart, Purchase)
- Deduplicated server + client event tracking
- Resilient tracking even when cookies are blocked
- Hashed email/phone support for audience matching
π§° Prerequisites
Requirement | Notes |
---|---|
OpenCart (v3.x or v4.x) | Full template + backend access |
Web GTM installed | Use header.twig |
Server-Side GTM container deployed | Prefer custom domain like gtm.yoursite.com |
Meta Pixel ID | From Events Manager |
Meta Access Token | From Business Settings > CAPI Setup |
π Step-by-Step Implementation
πΉ Step 1: Create Meta Pixel and Access Token
- Go to Meta Events Manager
- Create or select a Pixel
- Generate Access Token (under Conversions API Settings)
- Note:
- Pixel ID:
123456789012345
- Access Token:
EAABsb...
- Pixel ID:
πΉ Step 2: Setup Web GTM – Facebook Pixel Tags
In header.twig
, add GTM snippet:
<!-- Google Tag Manager -->
<script>
(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','GTM-XXXXXXX');
</script>
<!-- End Google Tag Manager -->
β Use this for standard GTM pixel injection.
πΉ Step 3: Push DataLayer Events in OpenCart
In product.twig
(ViewContent):
<script>
window.dataLayer = window.dataLayer || [];
dataLayer.push({
event: 'fb_view_content',
content_ids: ['{{ product.model }}'],
content_type: 'product',
value: '{{ product.price }}',
currency: '{{ session.currency }}',
content_name: '{{ product.name|escape("js") }}'
});
</script>
In success.twig
(Purchase):
<script>
dataLayer.push({
event: 'fb_purchase',
value: '{{ total }}',
currency: '{{ currency }}',
content_ids: [{% for p in products %}'{{ p.model }}'{% if not loop.last %},{% endif %}{% endfor %}],
content_type: 'product',
email: '{{ customer.email|lower|sha256 }}',
phone: '{{ customer.telephone|sha256 }}',
event_id: 'fbp_{{ order_id }}'
});
</script>
β
Hash email
and phone
in PHP using SHA256 before injecting into Twig.
πΉ Step 4: Setup Facebook Pixel Tags in Web GTM
Use Custom HTML Tags or Facebook Pixel Tag Template (preferred):
- ViewContent Tag
- Trigger:
fb_view_content
- Payload:
- Trigger:
fbq('track', 'ViewContent', {
content_ids: {{DL - content_ids}},
content_type: 'product',
value: {{DL - value}},
currency: {{DL - currency}}
}, {eventID: {{DL - event_id}} });
- Purchase Tag
- Trigger:
fb_purchase
- Same structure,
track('Purchase', {...})
- Trigger:
β Ensure each client event passes a unique event_id
πΉ Step 5: Setup Forwarding to Server-Side GTM
In the same event tag (Purchase), forward to ssGTM via transport_url
using GA4 or custom Fetch:
If using Custom HTTP Request Tag:
fetch("https://gtm.yoursite.com/event", {
method: "POST",
body: JSON.stringify({
event_name: "Purchase",
content_ids: {{DL - content_ids}},
value: {{DL - value}},
currency: {{DL - currency}},
event_id: {{DL - event_id}},
email: {{DL - email}},
phone: {{DL - phone}},
action_source: "website"
}),
headers: {
"Content-Type": "application/json"
}
});
β Or, send via GA4 tag and use Server GTM to proxy to Meta CAPI.
πΉ Step 6: Server GTM β Set Up Meta CAPI Tag
- Use Meta CAPI Tag Template
Link: Meta Tag in GTM Gallery - Fill Fields:
- Pixel ID:
123456789012345
- Access Token: From Meta
- Event Name:
{{Event Data β event_name}}
- event_id:
{{event_id}}
- email:
{{email}}
- phone:
{{phone}}
- currency/value/content_ids
- Pixel ID:
- Trigger:
Event Name equals Purchase
β You now have full server-side delivery with event deduplication logic.
πΉ Step 7: Deduplicate Server + Client Events
- Send event_id in both client (Pixel) and server (CAPI)
- In Meta Events Manager, verify:
- No duplicate
- CAPI shows as βProcessedβ
- Pixel + Server joined under same event
π§ͺ QA Tools
Tool | Purpose |
---|---|
GTM Preview (Web + Server) | Debug variables & flow |
Meta CAPI Test Events | Test server delivery |
Meta Events Manager β Diagnostics | Check deduplication |
Chrome DevTools β Network tab | Verify Pixel/Fetch requests |
π§ Pro Tips
Tip | Why It Helps |
---|---|
Use custom domain for ssGTM | Avoids ad blockers |
Pass consent flags if needed | GDPR/CCPA compliance |
Hash PII server-side | Avoid exposing raw data |
Enable automatic advanced matching | Fallback for missed PII |
Log server errors to GCP or webhook | Improves reliability |
π¦ Summary Architecture
[OpenCart Frontend]
β
[dataLayer β Web GTM]
β β
[Meta Pixel Tag] [Fetch to ssGTM]
β
[Server-Side GTM Container]
β
[Meta Conversions API]