Fix de colisão de pixel Twitter/X vs Google Ads em Shopify
Pixel do X sobrescrevia UTMs e gclid via history.replaceState(), quebrando atribuição do Google Ads. Guard script recuperou 100% da atribuição.
Contexto
O que é: debug e fix de colisão entre o pixel do Twitter/X e o rastreamento do Google Ads numa loja Shopify. O pixel do X reescrevia a URL via history.replaceState(), engolindo UTMs e gclid, e quebrando a atribuição no checkout. Guard script recuperou 100% da atribuição.
Contexto: loja DTC com Meta Ads, Google Ads e X Ads simultâneos. Atribuição no Google Ads começou a cair sem mudança nas campanhas, sintoma clássico de conflito entre tags cliente.
Problema
Pixel do X injetava parâmetros tw_source, tw_adid e similares via history.replaceState(), sobrescrevendo a URL e engolindo UTMs e gclid originais. O Google Ads perdia a atribuição no checkout.
Abordagem
Interceptar e proteger os parâmetros críticos antes que qualquer tag os modifique. Persistir em sessionStorage. Monitorar mutações na URL e re-aplicar se sobrescritas.
Execução
Script carregado no início do <head> via GTM, antes de qualquer outro pixel. Observer em history.pushState e history.replaceState.
(function attributionGuard() {
const CRITICAL = ['utm_source', 'utm_medium', 'utm_campaign',
'utm_content', 'utm_term', 'gclid', 'fbclid'];
const params = new URLSearchParams(window.location.search);
const saved = {};
CRITICAL.forEach((k) => {
if (params.has(k)) saved[k] = params.get(k);
});
if (Object.keys(saved).length) {
sessionStorage.setItem('sp_attribution', JSON.stringify(saved));
}
const restore = () => {
const current = new URLSearchParams(window.location.search);
let dirty = false;
Object.entries(saved).forEach(([k, v]) => {
if (!current.has(k)) {
current.set(k, v);
dirty = true;
}
});
if (dirty) {
const clean = `${window.location.pathname}?${current}`;
window.history.replaceState({}, '', clean);
}
};
// Intercepta mudanças de URL feitas por outros scripts
const originalReplace = history.replaceState;
history.replaceState = function (...args) {
originalReplace.apply(this, args);
restore();
};
window.addEventListener('DOMContentLoaded', restore);
})();Resultados
Output reutilizável
Documentação técnica do conflito (nível de browser), checklist de validação cross-pixel, snippet reaplicável em qualquer Shopify com stack similar.