Cookie first-party GA4 : récupérer les sessions Not Set

Procédure pour reconstruire l'attribution perdue en server-side : cookie first-party, variables GTM et dimensions personnalisées GA4.

Sur un setup GA4 server-side ou via Measurement Protocol, il n’est pas rare de voir 30 à 50 % des conversions classées en (not set). La cause : les hits arrivent au serveur sans le contexte navigateur (pas d’UTM, pas de referrer, pas de client_id natif), et toute l’attribution s’évapore. Sur un compte e-commerce que j’ai audité, le ratio mesuré était de 48 % de conversions Not Set — 410 achats sur 862 sans source identifiée.

La technique pour récupérer cette donnée existe et tient en quatre briques : un cookie d’attribution first-party, des variables GTM, des dimensions personnalisées GA4, et une séquence de validation. Voici la procédure complète, applicable telle quelle.

L’essentiel
  • Le problème vient du Measurement Protocol qui ignore le contexte navigateur.
  • La solution : capturer les UTM côté client dans un cookie first-party, puis les renvoyer en dimensions custom sur chaque hit.
  • Implémentation : 1 variable JS GTM + 1 cookie + 5 dimensions personnalisées GA4 + 1 modif du tag GA4.
  • Le temps total est de 2 à 3 heures, validation comprise.

Pourquoi le server-side perd l’attribution

En setup client-side classique, GA4 reçoit le hit directement depuis le navigateur de l’utilisateur. Il a accès au document.referrer, à l’URL avec ses paramètres UTM, aux cookies first-party historiques. L’attribution est calculée à partir de ces signaux.

En server-side ou Measurement Protocol, le hit transite par votre serveur GTM. Si la couche client ne fait pas son travail de transmission des données contextuelles, GA4 reçoit un événement décontextualisé. Pour les sessions où l’UTM n’a pas été propagé correctement, ou pour les conversions différées (plusieurs jours après la première visite), la source d’origine est perdue.

Cas concret observé sur un compte audité :

  • 862 achats totaux sur 30 jours
  • 452 achats avec source identifiée (52 %)
  • 410 achats en (not set) (48 %)
  • Les 443 achats trackés côté client-side correspondent presque exactement aux 452 attribués correctement — ce qui confirme que les 410 Not Set sont des hits server-side qui ont perdu le contexte

Sans correction, c’est 48 % du chiffre d’affaires qui circule sans attribution. Reporting marketing inutilisable, ROAS faussé, décisions budgétaires aveugles.

La logique de correction

L’idée est de capturer les UTM au moment où ils sont encore disponibles (côté client, dès l’arrivée du visiteur), de les stocker dans un cookie first-party qui survit aux sessions ultérieures, puis de les renvoyer comme dimensions custom sur chaque hit GA4 (y compris les hits server-side).

GA4 ne pourra toujours pas reconstituer ces données dans ses dimensions natives (source, medium, campaign), mais les dimensions custom les exposeront proprement dans les rapports et BigQuery.

Une variable JavaScript personnalisée gère la capture et la restitution.

Type : JavaScript personnalisé

function() {
  var COOKIE_NAME = '_te';           // nom du cookie
  var COOKIE_DURATION_DAYS = 30;     // durée de vie

  function getAttributionCookie() {
    var cookies = document.cookie.split(';');
    for (var i = 0; i < cookies.length; i++) {
      var c = cookies[i].trim();
      if (c.indexOf(COOKIE_NAME + '=') === 0) {
        try {
          return JSON.parse(decodeURIComponent(c.substring(COOKIE_NAME.length + 1)));
        } catch (e) { return null; }
      }
    }
    return null;
  }

  function setAttributionCookie(data) {
    var d = new Date();
    d.setDate(d.getDate() + COOKIE_DURATION_DAYS);
    document.cookie = COOKIE_NAME + '=' +
      encodeURIComponent(JSON.stringify(data)) +
      ';expires=' + d.toUTCString() + ';path=/;SameSite=Lax';
  }

  function getUTMParameters() {
    var p = new URLSearchParams(window.location.search);
    var utm = {
      utm_source: p.get('utm_source') || '',
      utm_medium: p.get('utm_medium') || '',
      utm_campaign: p.get('utm_campaign') || '',
      utm_content: p.get('utm_content') || '',
      utm_term: p.get('utm_term') || '',
      timestamp: Date.now()
    };
    // Fallback : si pas d'UTM, lire le referrer
    if (!utm.utm_source && document.referrer) {
      try {
        var ref = new URL(document.referrer);
        utm.utm_source = ref.hostname.replace('www.', '');
        utm.utm_medium = 'referral';
      } catch (e) {}
    }
    return utm;
  }

  var existing = getAttributionCookie();
  var current = getUTMParameters();

  // Logique de priorité : on n'écrase QUE si un nouvel UTM est présent
  if (current.utm_source && (!existing || current.utm_source !== existing.utm_source)) {
    setAttributionCookie(current);
    return current;
  }
  return existing || current;
}

Trois logiques clés à comprendre :

  1. Persistance 30 jours — l’utilisateur peut revenir 3 semaines plus tard, l’attribution est préservée
  2. Fallback referrer — si l’utilisateur arrive sans UTM mais depuis un site externe, on capture le referrer comme source
  3. Priorité au nouveau touchpoint — un nouveau click identifié écrase le précédent (logique “last paid click” implicite)

Étape 2 — Exposer chaque UTM comme variable GTM séparée

Le cookie renvoie un objet JSON. Il faut une variable GTM par dimension à renvoyer à GA4. Cinq variables, type JavaScript personnalisé, qui s’appuient sur la variable maître.

Exemple pour utm_source :

function() {
  var attr = {{JS - Cookie Attribution}};
  return attr && attr.utm_source ? attr.utm_source : '(not set)';
}

Répéter pour utm_medium, utm_campaign, utm_content, utm_term. Nommer proprement : JS - UTM Source, JS - UTM Medium, etc.

Étape 3 — Déclarer les dimensions personnalisées dans GA4

Direction Admin → Définitions personnalisées → Dimensions personnalisées → Créer.

Nom de la dimensionScopeParamètre d’événement
Custom UTM SourceEventcustom_utm_source
Custom UTM MediumEventcustom_utm_medium
Custom UTM CampaignEventcustom_utm_campaign
Custom UTM ContentEventcustom_utm_content
Custom UTM TermEventcustom_utm_term

Attention aux limites GA4 : 50 dimensions event-scoped maximum par propriété. Si vous êtes déjà proche du plafond, planifier l’usage.

Étape 4 — Modifier le tag GA4 pour envoyer les paramètres

Sur votre tag GA4 Event Configuration (le tag de config), ajouter dans la section “Event parameters” :

Parameter nameValue
custom_utm_source{{JS - UTM Source}}
custom_utm_medium{{JS - UTM Medium}}
custom_utm_campaign{{JS - UTM Campaign}}
custom_utm_content{{JS - UTM Content}}
custom_utm_term{{JS - UTM Term}}

Si vous avez un container server-side, renvoyer ces mêmes paramètres dans le client GA4 du serveur GTM. Sans ça, les hits server-side perdront à nouveau l’information.

Étape 5 — Valider en DebugView + monitoring

Test immédiat

  • Activer le mode preview GTM et le DebugView GA4
  • Visiter le site avec ?utm_source=test&utm_medium=cpc&utm_campaign=validation
  • Vérifier dans DebugView que les 5 paramètres custom_utm_* sont présents sur chaque event
  • Fermer le navigateur, rouvrir 10 minutes plus tard sans UTM : vérifier que le cookie _te est toujours présent et que les paramètres custom continuent de remonter

Monitoring 7 à 14 jours

  • Créer un rapport personnalisé avec la dimension Custom UTM Source et la métrique Conversions
  • Comparer la part de (not set) sur la dimension native Session source vs la dimension Custom UTM Source
  • Sur les comptes que j’ai migrés, la dimension custom remonte généralement 75 à 90 % des cas qui étaient en Not Set natif

Précision importante

La dimension native (not set) ne disparaît pas. Le cookie d’attribution alimente une dimension parallèle plus complète. Le reporting “vrai” passe désormais par cette nouvelle dimension. C’est exactement la logique de la Cause 4 détaillée dans l’article résoudre le (not set) dans GA4.

Cas particuliers à anticiper

Sites cross-domain

Si l’utilisateur transite entre marque.fr et shop.marque.fr, le cookie _te doit être défini sur le domaine racine. Adapter la ligne du cookie :

document.cookie = COOKIE_NAME + '=' + ... + ';path=/;domain=.marque.fr;SameSite=Lax';

Conformité RGPD

Le cookie _te est un cookie d’attribution. En toute rigueur, il doit être soumis au consentement marketing/analytique selon votre interprétation. Vérifier que votre Consent Mode v2 ne bloque pas l’écriture du cookie quand le consentement est accordé.

Server-side strict

Si vous voulez envoyer aussi ces UTM côté Meta CAPI ou Microsoft Ads, ajouter les mêmes paramètres dans les tags correspondants. La logique reste : capture côté client, persistence cookie, restitution sur tous les hits.

Mise en perspective

Cette procédure couvre la Cause 4 du Not Set en GA4 (attribution perdue en server-side). Pour les autres causes — high cardinality, désalignement de scope, Enhanced Measurement mal configuré — voir l’article général résoudre le (not set) dans GA4. Si la suite logique est de corriger les classifications natives de GA4 (Paid Search en Organic, etc.), passer aux canaux personnalisés GA4. Et pour le choix du modèle d’attribution qui exploitera ces données propres, voir les 6 modèles d’attribution.

Sur les setups e-commerce complexes, cette procédure est l’une des composantes d’un audit tracking complet. La méthodologie globale est détaillée dans la petite-fille méthodologie d’audit.

En synthèse

48 % de conversions Not Set n’est pas une fatalité du server-side. Avec un cookie first-party, 5 variables GTM et 5 dimensions custom, on récupère la quasi-totalité de l’attribution perdue. Le coût d’implémentation est de quelques heures ; le gain en qualité de pilotage marketing dure des années.

Sources

Besoin d'aide sur ce sujet ?

Je peux vous accompagner sur la mise en place ou l'optimisation de votre tracking.

Sans surprise : forfaits affichés en clair, devis validé avant kick-off, pas d'avenant.