With privacy regulations like GDPR, CCPA, and browser changes (e.g., Safariβs ITP, Firefox ETP), traditional client-side tracking is no longer reliable. Users block cookies, disable JavaScript, or opt out of tracking entirely.
Server-side tracking provides a robust, privacy-conscious solution to improve attribution, ensure compliance, and deliver high-quality data across GA4, Google Ads, Meta CAPI, and other platforms.
π― Goals of Server-Side Tracking
- Restore conversion attribution even when client-side is blocked
- Respect consent while improving data fidelity
- Maintain ad platform visibility (Google Ads, Meta Ads)
- Enable first-party data enrichment and modeling
π§° Requirements
- A deployed Server-Side GTM container (
gtm.yourdomain.com) - GA4 and Google Ads accounts
- Consent management platform (CMP) or custom consent system
- Web GTM container installed on site
- OpenCart (or any other eCommerce platform) with customization access
π Step-by-Step Guide
πΉ Step 1: Deploy Your Server-Side GTM Container
Use Google Cloud App Engine or a provider like Stape.io:
https://github.com/GoogleCloudPlatform/terraform-google-tag-manager
Example endpoint:https://gtm.yourdomain.com
β Set this domain as a first-party cookie server for better attribution.
πΉ Step 2: Update GA4 and Conversion Tags to Use Transport URL
In Web GTM:
- Modify your GA4 Configuration tag:
- Add field:
transport_url=https://gtm.yourdomain.com
- Add field:
- Modify Google Ads / Meta tags to also send data server-side (if applicable)
β This routes tracking data through your server endpoint.
πΉ Step 3: Setup Consent-Aware Data Collection
Ensure that user consent is respected before sending data to the server:
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
event: "default_consent",
ad_storage: "denied",
analytics_storage: "denied"
});
// After consent given
function onConsentGranted() {
dataLayer.push({
event: "update_consent",
ad_storage: "granted",
analytics_storage: "granted"
});
}
β Integrate with your CMP (e.g., Cookiebot, OneTrust) to update consent dynamically.
πΉ Step 4: Capture User Identifiers (GCLID, FBC, Client_ID)
Use JS to extract and store first-party identifiers:
<script>
(function(){
const urlParams = new URLSearchParams(window.location.search);
const gclid = urlParams.get('gclid');
const fbc = urlParams.get('fbclid');
if (gclid) document.cookie = `gclid=${gclid}; path=/; max-age=2592000; SameSite=Lax`;
if (fbc) document.cookie = `fbc=${fbc}; path=/; max-age=2592000; SameSite=Lax`;
})();
</script>
β These cookies are used server-side to match sessions with ad clicks.
πΉ Step 5: Send Conversion Events from Client to Server
Example purchase push (in success.twig):
<script>
window.dataLayer = window.dataLayer || [];
dataLayer.push({
event: 'purchase',
ecommerce: {
transaction_id: '{{ order_id }}',
value: '{{ total }}',
currency: '{{ currency }}',
items: [
{% for product in products %}
{
item_id: '{{ product.model }}',
item_name: '{{ product.name|escape('js') }}',
price: '{{ product.price }}',
quantity: '{{ product.quantity }}'
}{% if not loop.last %},{% endif %}
{% endfor %}
]
},
user_data: {
email: '{{ customer.email|lower|sha256 }}',
phone: '{{ customer.telephone|sha256 }}',
client_id: '{{ ga_client_id }}',
gclid: '{{ gclid_cookie }}',
fbc: '{{ fbc_cookie }}'
},
event_id: 'oc_{{ order_id }}'
});
</script>
β Use SHA256 hashing in PHP or JS for PII (email, phone) before pushing.
πΉ Step 6: Handle Events in Server GTM
In Server-Side GTM:
- Create Variables:
ecommerce.valueuser_data.emailevent_id
- Create Tags:
- GA4 Server Tag β Sends to GA4
- Google Ads Conversion Tag β Sends to Google Ads
- Meta CAPI Tag (via HTTP Request Tag or Template)
Trigger Condition:Event Name equals "purchase" and Consent granted = true
πΉ Step 7: Add HTTP Headers for Attribution Enrichment
Enable Request Headers in server GTM to read:
User-AgentIP AddressReferer
β This enhances matching accuracy in Meta, GA4, and Google Ads.
πΉ Step 8: Implement Conversion Deduplication
In all purchase events, include:
"event_id": "oc_{{order_id}}"
In server-side tags (Google Ads, GA4, Meta), map event_id so platforms can deduplicate conversions from multiple sources (e.g., client + server).
π§ͺ QA Checklist
| Item | β |
|---|---|
GA4 uses transport_url |
β |
| Event sent from client to server | β |
| Server GTM receives and processes | β |
| Consent checked before firing | β |
| event_id used for deduplication | β |
| Headers available in request | β |
| Matching identifiers present (gclid, client_id) | β |
π§ Pro Tips
| Tip | Why It Matters |
|---|---|
| Use hashed PII for privacy | Enables matching without storing raw data |
| Set up logging in Server GTM | Audit conversion flow |
| Whitelist trusted domains | Prevent spoofed requests |
| Monitor via GA4 DebugView + GTM Server Preview | Real-time validation |
| Always fallback to client-only if user denies consent | Legal compliance |
π¦ Architecture Summary
[Client Browser]
β
[dataLayer β Web GTM]
β β
[Consent Check] [GA4 Tag β transport_url=https://gtm.yourdomain.com]
β
[Server-Side GTM Container]
β β β
[GA4] [Google Ads] [Meta CAPI]
