In the modern privacy-first landscape, preserving UTM parameters across a user’s OpenCart session is crucial for accurate attribution, funnel analysis, and marketing ROI. Most users land with UTM tags (like from Google Ads), but lose attribution by the time they convert.
๐งฐ Prerequisites
| Component | Purpose |
|---|---|
| OpenCart 3.x or 4.x | eCommerce platform |
| Google Tag Manager | Installed across all pages |
| Google Analytics 4 | Configured via GTM |
| Consent Mode v2 | Optional but recommended for legality |
๐ UTM Parameters Weโll Track
utm_sourceutm_mediumutm_campaignutm_contentutm_term
๐ฆ Step 1: Create a UTM Capture Script in OpenCart Header
Insert this script in catalog/view/theme/YOUR_THEME/template/common/header.twig:
<script>
(function() {
const getParam = (name) => {
const match = new RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
return match && decodeURIComponent(match[1].replace(/\+/g, ' '));
};
const utmParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content', 'utm_term'];
utmParams.forEach(param => {
const value = getParam(param);
if (value) {
localStorage.setItem(param, value);
document.cookie = `${param}=${value}; path=/; max-age=2592000`; // 30 days
}
});
})();
</script>
โ
This stores the UTM values in both localStorage and cookie, making them accessible for both client-side and server-side scripts.
๐๏ธ Step 2: Push Stored UTM Values into dataLayer
Still inside header.twig or footer.twig, add:
<script>
window.dataLayer = window.dataLayer || [];
dataLayer.push({
event: 'utm_ready',
utm_source: localStorage.getItem('utm_source') || '',
utm_medium: localStorage.getItem('utm_medium') || '',
utm_campaign: localStorage.getItem('utm_campaign') || '',
utm_content: localStorage.getItem('utm_content') || '',
utm_term: localStorage.getItem('utm_term') || ''
});
</script>
๐ Step 3: Configure GTM to Capture These Values
In GTM:
- Create Data Layer Variables:
DLV - utm_sourceโutm_sourceDLV - utm_mediumโutm_mediumDLV - utm_campaignโutm_campaignDLV - utm_contentโutm_contentDLV - utm_termโutm_term
- Modify GA4 Event Tags (purchase, add_to_cart, etc.):
- Add custom parameters in the event tag:
utm_source: {{DLV - utm_source}} utm_medium: {{DLV - utm_medium}} utm_campaign: {{DLV - utm_campaign}}
- Add custom parameters in the event tag:
- Optionally send these as user properties in your GA4 Config tag:
user_properties.utm_source: {{DLV - utm_source}}
โ Step 4: Inject UTM into Purchase Event on Success Page
In success.twig, ensure this is fired with each order:
<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.product_id }}',
item_name: '{{ product.name }}',
price: '{{ product.price }}',
quantity: '{{ product.quantity }}'
}{% if not loop.last %},{% endif %}
{% endfor %}
]
},
attribution: {
utm_source: localStorage.getItem('utm_source'),
utm_medium: localStorage.getItem('utm_medium'),
utm_campaign: localStorage.getItem('utm_campaign'),
utm_content: localStorage.getItem('utm_content'),
utm_term: localStorage.getItem('utm_term')
}
});
</script>
Then in GTM, update your purchase GA4 tag to include:
utm_source: {{DLV - attribution.utm_source}}utm_medium: {{DLV - attribution.utm_medium}}- etc.
๐ Step 5: Consent-Aware Storage (Optional)
If using a CMP (like Cookiebot), wrap the script like:
<script>
if (window.Cookiebot && Cookiebot.consents.given.marketing) {
// store or read UTM
}
</script>
Or use GTM’s consent settings to delay UTM storage until allowed.
๐งช Step 6: QA & Debugging
| Tool | Use Case |
|---|---|
| GTM Preview Mode | Confirm UTM variables are available |
| GA4 DebugView | Validate custom UTM parameters in events |
| Browser DevTools | Inspect localStorage + cookies |
| GA4 โ Explorations | Build audience filters by UTM data |
๐ Strategic Value of UTM Persistence
| Benefit | Why It Matters |
|---|---|
| Survives multi-day journeys | Users can convert days later with credit |
| Enables retargeting segmentation | Segment by campaign even after bounce |
| Fixes attribution gaps | Avoids (direct) / (none) in GA4 |
| Powers server-side attribution | UTM from cookies = backend use |
