Qu’est-ce que le “not set” dans GA4 ?
Dans Google Analytics 4, “not set” n’est pas un bug mystique, c’est un aveu brutal : GA4 n’a pas la moindre idée d’où vient votre trafic.
C’est l’équivalent analytique de “je sais pas, j’étais pas là”.
Concrètement, dans vos rapports d’acquisition, la colonne Source / Support affiche “(not set)” quand les informations censées identifier l’origine du visiteur — d’où il vient, comment il est arrivé — sont introuvables ou perdues en route. Résultat : au lieu d’avoir un beau “google / organic” ou “newsletter / email”, vous vous retrouvez avec… rien.
Pourquoi c’est grave ? Parce que l’attribution disparaît. Et sans attribution, votre tableau de bord devient un roman policier où le coupable a été effacé des caméras. Vous perdez la trace des campagnes, le ROI devient un concept philosophique, et vos décisions marketing se basent sur des chiffres aussi fiables qu’une horloge fondue de Salvador Dalí.
Erreur classique : accuser Google. Mauvaise nouvelle, 80 % du temps, ce n’est pas Google le problème. C’est votre implémentation. Ou celle de votre agence. Ou celle du développeur qui, persuadé d’optimiser, a déclenché un tag avant le tag principal, comme on démarre une voiture avant d’avoir mis l’essence.
En clair :
Formez-vous à Google Analytics !
Maîtriser Google Analytics est crucial pour comprendre votre audience, optimiser vos campagnes, améliorer l'expérience utilisateur et mesurer vos conversions... Gagnez du temps avec nos formations Google Analytics.
- “Not set” = données d’origine perdues.
- Impact = attribution faussée, KPIs inutilisables, décisions bancales.
- Cause principale = configuration ou déclenchement technique mal pensé.
C’est rarement une fatalité, mais c’est souvent un mélange explosif de mauvais timing, mauvaise config, et mauvaise communication.
- Qu'est-ce que le “not set” dans GA4 ?
- Hypothèse 1 : Mauvais ordre de déclenchement des tags ga4
- Hypothèse 2 : Implémentation serveur-side mal configurée
- Hypothèse 3 : Measurement protocol mal utilisé
- Hypothèse 4 : Problème de l’event “session_start” manquant
- Hypothèse 5 : Audience triggers : le faux bon plan
- Hypothèse 6 : Paramètres utm incorrects
- Hypothèse 7 : Données récentes non traitées
- Hypothèse 8 : Trafic spam/bots
- Conclusion
Hypothèse 1 : Mauvais ordre de déclenchement des tags ga4
Un système de mesure, c’est d’abord un ordre de marche. En GA4, la balise de configuration ouvre la session, dépose les identifiants, prépare le contexte. Les event tags racontent ensuite ce qui se passe. Inversez l’ordre, et vous envoyez des événements sans identité. Résultat : source / support perdus, sessions éclatées, (not set) qui grimpe.
Le problème (factuel, pas philosophique)
- Event avant config : un
purchase
ou ungenerate_lead
part en éclaireur pendant que la config roupille. - Conséquence immédiate : pas de
session_id
valable, pas d’héritage de campagne → (not set) dans Acquisition. - Portrait-robot : vous voyez des événements dans DebugView, mais pas de
session_start
juste avant.
La solution courte
- La config GA4 doit partir en premier. Toujours.
- Les events attendent. Ils se déclenchent après la config, et idéalement dans une séquence forcée.
Implémentation propre dans gtm web
- Balise GA4 Configuration
- Déclencheur :
Initialization – All Pages
(pour partir le plus tôt possible). - Consent Mode : si CMP, mettez la CMP en Consent Initialization, puis conditionnez la config à l’accord, ou acceptez le mode “pings” en connaissance de cause.
- Option utile en SPA :
send_page_view: false
+ envoi manuel dupage_view
(évite des vues fantômes).
- Déclencheur :
- Balises GA4 Event
- Déclencheurs :
All Pages
, clics, dataLayer… mais jamais surInitialization
. - Tag sequencing : Advanced Settings → Tag Sequencing → Fire a tag before this tag → votre GA4 Configuration.
- En server-side : renseignez Server Container URL partout (config et events).
- Déclencheurs :
- Anti-patterns à bannir
- Event en
Initialization
. - Config en
Page View
et events enDOM Ready
(ça peut encore partir avant). - Mélange GTM + gtag hard-codé qui tire des events avant la config.
- Event en
Variante si vous utilisez gtag.js “hard-codé”
L’ordre des appels doit être config
→ event
. Exemple minimal correct :
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-XXXXXXX', {
// en server-side : forcez le transport via votre endpoint
transport_url: 'https://collect.votredomaine.com'
});
// Les events viennent APRÈS la config :
gtag('event', 'generate_lead', {value: 1});
</script>
Si la page est SPA, envoyez manuellement un page_view
à chaque changement de route, après la config.
Diagnostic express (2 minutes)
- GTM Preview + GA4 DebugView : sur une page fraîche, l’ordre attendu est
session_start
→page_view
→ vos events. - Onglet Réseau : filtrez
g/collect
. Le premier hit doit êtreen=session_start
. Si un event part avant, l’ordre est mauvais. - Rapport Acquisition : filtrez un event clé. Si
(not set)
chute brutalement après ré-ordonnancement, vous avez trouvé le coupable.
Cas particuliers à ne pas louper
- CMP lente : si la config attend 2 secondes le consentement, l’utilisateur peut cliquer et déclencher des events “trop tôt”. Solution : déclenchez la CMP en Consent Initialization et réduisez la latence.
- SPA : première charge = config +
page_view
. Changements de route =page_view
manuel. Un seulsession_start
au tout début. - Server-side : sans
server_container_url
homogène, certains hits partent en direct, d’autres via serveur → ordre et identités cassés.
Check-list “zéro not set” pour l’ordre de tir
- Config GA4 en Initialization
- Events jamais en Initialization
- Tag sequencing activé (event → “fire GA4 config before”)
- CMP en Consent Initialization puis config conditionnée
- En server-side : Server Container URL présent sur toutes les balises
- SPA :
page_view
manuel sur les routes,send_page_view
maîtrisé
Aphorisme utile : “On n’appuie pas sur le déclencheur avant d’armer la mesure.” Une fois l’ordre respecté, la source et le medium reviennent sagement, et vos rapports cessent de faire du dadaïsme.
Hypothèse 2 : Implémentation serveur-side mal configurée
Le server-side GTM, c’est un proxy propre pour vos hits. À une condition non négociable : tout doit passer par lui. Le petit paramètre qui fait la loi s’appelle server_container_url
(côté GTM Web) — alias l’adresse de votre endpoint (https://collect.votredomaine.com
). Oubliez-le sur un seul tag, et vous créez des hits qui partent directement chez Google pendant que le reste passe par votre serveur. Mélange des genres = sessions qui se découpent, contextes perdus, et une moisson de (not set).
Pourquoi l’oubli de server_container_url
casse l’attribution
- Deux routes, deux identités.
Le conteneur serveur peut gérer un client_id first-party (FPID) et uniformiser les cookies. Un hit envoyé en direct (sans passer par le serveur) n’a pas ce même identifiant. Résultat : nouvelle session, parfois sanssession_id
cohérent → événements orphelins → (not set). - Ordre et timing désalignés.
Les hits web (via serveur) et les hits directs n’arrivent pas dans la même chronologie technique. GA4 ne les regroupe pas proprement → perte de la source / medium héritée. - Configs divergentes.
Côté serveur, vous appliquez souvent des règles (enrichissement, filtres, réécriture) qui n’existent pas quand un hit part en direct. Incohérence = attribution bancale.
Symptômes typiques
- La majorité des événements est OK, une fraction aléatoire tombe en (not set).
- L’exploration en temps réel montre des hits qui pointent tantôt vers
https://collect.votredomaine.com/g/collect
, tantôt vershttps://www.google-analytics.com/g/collect
. - En environnement avec Server-Managed Client ID (FPID) : explosion des self-referrals ou sessions doublées dès qu’un hit file en direct.
La bonne pratique (pas “optionnelle”) : homogénéiser partout
- Dans GTM Web → balise GA4 Configuration
- Renseignez Server Container URL :
https://collect.votredomaine.com
- Activez le tag sequencing pour forcer cette config avant tous les events.
- Renseignez Server Container URL :
- Dans toutes les balises GA4 Event
- Ajoutez Server Container URL (même valeur).
- Oui, l’héritage “devrait” suffire ; non, on ne mise pas une attribution dessus.
- Si vous avez du gtag.js “hard-codé” dans le code source
- Ajoutez le transport côté gtag : htmlCopyEdit
<script> window.dataLayer = window.dataLayer || []; function gtag(){dataLayer.push(arguments);} gtag('js', new Date()); gtag('config', 'G-XXXXXXX', { transport_url: 'https://collect.votredomaine.com' }); </script>
- Ou mieux : supprimez le hard-code et migrez tout dans GTM Web.
- Ajoutez le transport côté gtag : htmlCopyEdit
- Measurement Protocol (serveur → GA4)
- Réutilisez le même couple
client_id
+session_id
que la session web active. - Si vous utilisez FPID côté serveur, assurez la cohérence côté web (tous les hits via le serveur, jamais en direct).
- Réutilisez le même couple
- DNS & cookies
- Pointez
collect.votredomaine.com
vers votre conteneur serveur (A/AAAA ou CNAME propre). - Si vous utilisez FPID, validez que le cookie first-party est bien posé sur le bon domaine (et TTL attendu).
- Pointez
- Exclusion d’auto-référent
- Ajoutez votre sous-domaine serveur dans Admin → Flux de données → Liste des références non désirées pour éviter que
collect.votredomaine.com
apparaisse comme source parasite.
- Ajoutez votre sous-domaine serveur dans Admin → Flux de données → Liste des références non désirées pour éviter que
Contrôle qualité (5 minutes chrono)
- Network (onglet Réseau) : filtrez
g/collect
. Tous les appels doivent cibler votre domaine serveur. Zéro appel direct versgoogle-analytics.com
. - DebugView GA4 : vérifiez que
session_start
puispage_view
et vos events arrivent dans la même session. - Journal serveur (GTM Server) : le client GA4 doit traiter 100 % des hits. Les 404/Pass-through = alerte.
- Comparatif avant/après : le taux de (not set) en Acquisition doit chuter après homogénéisation. Mesurez hors 48 h pour éviter le bruit d’ingestion.
Cas tordus (et correctifs)
- Mix GTM Web + vieux script GA “planqué”
→ Fouillez le code, CMS, balises inline. Un seul chemin autorisé : via le serveur. - CMP/Consent lancée après la config
→ Lancez la CMP en Consent Initialization, puis la config GA4 conditionnée ; le serveur ne sauvera pas un hit lancé trop tôt… ou trop tard. - SPA (React/Vue) + server-side
→ Première vue : config + page_view en Initialization (via serveur). Changements de route : events manuels, toujours via serveur.
Mini tableau mémo
Erreur | Symptôme | Correctif |
---|---|---|
server_container_url manquant sur 1+ tags | Pics de (not set) aléatoires | Renseigner l’URL sur toutes les balises (config + events) |
gtag hard-codé sans transport_url | Requêtes mixtes (serveur + direct) | Ajouter transport_url ou retirer le hard-code |
FPID activé + hits directs résiduels | Sessions dupliquées, self-referrals | Forcer 100 % des hits via serveur |
MP sans client_id /session_id web | Événements hors session → (not set) | Passer les IDs web au backend, aligner MP |
Aphorisme utile : “Une seule porte d’entrée, sinon c’est la fête du slip.”
Traduction pro : un endpoint unique, cohérent, obligatoire. Quand tout transite par votre conteneur serveur, l’attribution redevient lisible, et “(not set)” retourne à sa niche.
Hypothèse 3 : Measurement protocol mal utilisé
Le Measurement Protocol est l’API qui permet d’envoyer des événements GA4 depuis un serveur. Utile pour compléter une session web avec des infos back-office (paiement confirmé, stock, CRM). S’il manque les bons identifiants de session/utilisateur, vos hits deviennent des fantômes et l’acquisition finit en (not set).
Les erreurs classiques (qui ruinent l’attribution)
- Client ID inventé
Non,client_id: "1234"
ne “marchera pas”. GA4 attend le même client_id que celui posé côté navigateur. Sinon, impossible de rattacher l’événement à la bonne personne → orphelin. - Session ID inexistant… ou en texte
Lesession_id
doit être numérique et réel (celui de la session en cours). Unsession_id: "abc123"
ou imaginaire ne sera pas accepté ou ne rattachera rien → (not set). session_start
manquant
Le MP ne “crée” pas magiquement une session exploitable. Si vous envoyez un événement serveur avant qu’une session web ait démarré (ou longtemps après), rien à raccrocher → (not set). Évitez d’“émuler”session_start
via MP : ce n’est pas fiable, laissez le web le générer.- Horodatage hors fenêtre
Untimestamp_micros
trop ancien (> 72 h) ne sera pas rattaché à la session passée. Même combat : (not set) ou hit ignoré. - Payload incomplet
Oublierengagement_time_msec
(≥ 1) ou mélanger de mauvaismeasurement_id
/api_secret
peut faire jeter le hit. Ce n’est pas “not set”, c’est “pas pris”.
Comment éviter le massacre : récupérer les bons IDs depuis le cookie GA
Objectif : envoyer le même client_id
et le session_id
actif que la couche web. Deux options simples.
Option A — côté web (recommandée), puis passez au serveur :
<script>
// Récupérer le client_id via gtag (GA4 doit être chargé)
gtag('get', 'G-XXXXXXXX', 'client_id', function(cid) {
// exposez cid à votre backend (XHR, header, dataLayer, etc.)
console.log('client_id', cid);
});
// Récupérer le session_id via le cookie _ga_<MEASUREMENT_ID>
(function() {
const name = '_ga_XXXXXXXX'; // _ga_<MEASUREMENT_ID>
const cookie = document.cookie.split('; ').find(c => c.startsWith(name + '='));
if (!cookie) return;
const val = decodeURIComponent(cookie.split('=')[1]);
// La valeur contient des paires clé=valeur, cherchez "sid"
// Exemple (simplifié): GS1.1.1723549876.1.1.1723549876.0.0.0
// Certains environnements exposent sid séparément; sinon, lisez-le via l'API web GA4 si dispo.
})();
</script>
Astuce pratique : beaucoup d’implémentations exposent sid
via l’API web (variables GTM, DataLayer). Sinon, stockez le sid
(session id) quand GA4 déclenche session_start
et transmettez-le au backend pendant la session (cookie first-party, en-tête, champ masqué).
Option B — via cookie _ga
(fallback)
Le cookie _ga
contient le client_id sous forme xxxxxxxxxx.yyyyyyyyyy
(deux entiers séparés par un point). Pour le session_id, préférez la valeur sid
du cookie _ga_<MEASUREMENT_ID>
quand elle est accessible. Évitez de “deviner” : un sid
faux = (not set).
Exemple de requête Measurement Protocol correcte
-X POST "https://www.google-analytics.com/mp/collect?measurement_id=G-XXXXXXXX&api_secret=YOUR_API_SECRET" \
-H "Content-Type: application/json" \
-d '{
"client_id": "1234567890.987654321",
"timestamp_micros": 1723549876000000,
"non_personalized_ads": false,
"events": [{
"name": "purchase_server",
"params": {
"session_id": 1723549,
"session_number": 3,
"transaction_id": "ORD-5842",
"value": 129.9,
"currency": "EUR",
"engagement_time_msec": 1
}
}]
}'
Points clés :
client_id
= celui du navigateur (pas un ID CRM).session_id
= numérique et actif au moment de l’événement web correspondant.timestamp_micros
= dans la fenêtre des 72 heures et cohérent avec la session.engagement_time_msec
≥ 1 pour éviter les rejets silencieux.- Ne taggez pas ça en “conversion” si vous n’êtes pas certain du rattachement à une vraie session.
Recette simple et robuste (web ↔ serveur)
- Au
session_start
web, lisez et stockez côté client :client_id
,session_id
,session_number
, l’horodatage. - Exposez-les à votre backend (en-tête HTTP, token, champ masqué sur le checkout).
- Côté serveur, quand vous envoyez un hit MP (ex. confirmation de paiement) :
- réutilisez exactement ces valeurs,
- ajoutez
timestamp_micros
(temps réel ou léger backdating), - mettez
engagement_time_msec: 1
.
- Logguez la requête et la réponse MP (pour auditer les ratés).
- Vérifiez dans DebugView : l’événement MP doit tomber dans la même session (même
sid
) que le page_view/click correspondant.
Anti-patterns à bannir
- “On met
user_id
, ça suffira” → non, user_id n’aide pas l’attribution si client_id/session_id sont faux. - “On envoie MP d’abord, le web suivra” → vous fabriquez des hits hors session.
- “On crée un faux
session_start
côté serveur” → comportement non garanti, et souvent contre-productif.
Aphorisme utile : “Un hit sans client ni session, c’est une bouteille à la mer. Elle flotte, mais elle n’arrive jamais au port.”
Respectez le duo client_id + session_id réels, gardez l’horloge dans les clous, et votre Measurement Protocol cessera d’alimenter le cimetière des (not set).
Hypothèse 4 : Problème de l’event “session_start” manquant
session_start
est l’impulsion électrique du cerveau GA4. Sans elle, pas de session, pas d’attribution, juste des événements errants classés en (not set). Quand ça disparaît, ce n’est pas “le destin”, c’est une configuration qui boîte.
Ce qui peut le faire sauter
- Consentement mal géré (CMP / Consent Mode v2)
Sianalytics_storage
est denied ou accordé trop tard, GA4 ne lit/écrit pas les cookies au bon moment. Résultat : pas de session exploitable, donc pas desession_start
.
Réflexe : en GTM, déclencher la CMP en “Consent Initialization”, puis la config GA4 uniquement après consentement accordé (ou avec un mode “pings” assumé et analysé comme tel). - Ordre de tags inversé
La config GA4 doit partir avant tout event. Si un event part en Initialization et la config en Page View, vous tirez dans le vide.
Réflexe : config GA4 sur Initialization, events après (tag sequencing activé). - Bloqueurs / DNS filtrants
Brave, uBlock, Pi-hole, DNS “privacy” : ils coupentgtag.js
oug/collect
. Si rien n’atteint GA,session_start
n’existe pas.
Réflexe : passer par server-side tagging sur un sous-domaine first-party (collect.votredomaine.com
) et renseignertransport_url
. Testez avec un navigateur “propre” et un navigateur “blindé”. - Lenteur et précarité réseau
Sur mobile et réseaux faibles, l’utilisateur repart avant que la config GA4 ait tiré.
Réflexe : déclenchez la config le plus tôt possible (Initialization), nettoyez le bloat JS, mesurez le TTFB / LCP. Un tag qui démarre trop tard ne démarre pas. - SPA / navigation côté client mal trackée
Sur React/Vue/Angular, si la première vue n’envoie pas le bon enchaînement (config + page_view) ou si les changements de route ne sont pas gérés, la session peut rester fantôme.
Réflexe :- première charge : config GA4 + (optionnel)
send_page_view: true
- changements de route : event
page_view
manuel avecpage_location
/page_referrer
. - GTM : déclencheur History Change + variables d’URL.
- première charge : config GA4 + (optionnel)
- Measurement Protocol envoyé trop tôt / mal renseigné
Un événement serveur arrive avant qu’une session web existe, ou sans client_id / session_id réels → impossible à rattacher.
Réflexe : ne postez via MP qu’avec client_id + session_id d’une session active (ou assumez que c’est hors session et ne marquez pas conversion). Utiliseztimestamp_micros
pour caler l’ordre dans la fenêtre des 72 h. - Cookies inaccessibles ou isolés
Iframes sandbox, sous-domaines sans cross-domain configuré, ou politiques de cookies trop restrictives : GA4 ne peut pas persister l’ID → sessions volatiles.
Réflexe : configurer cross-domain linking si besoin, éviter les iframes sandbox pour la page de tracking, vérifier le domaine du cookie. - Config du site qui “mange” l’événement
Pages qui redirigent instantanément, auto-refresh agressif, scripts qui écrasentdataLayer
… L’événement n’a pas le temps de vivre.
Réflexe : stabiliser le DOM avant redirection, push dansdataLayer
sans le réinitialiser, éviter les hard-refresh côté JS.
Le seuil de douleur
- 1–2 % de sessions sans
session_start
: bruit acceptable. - > 5 % : problème structurel. Auditez d’urgence l’enchaînement Consent → Config → Events et les conditions réseau/navigateur.
Check express (5 minutes)
- GTM Preview + GA4 DebugView
Rechargez une page. Vous devez voir, en tête,session_start
suivi depage_view
. S’il manque, regardez l’ordre et les triggers. - Onglet Réseau
Filtrez surg/collect
. Vérifiez l’eventen=session_start
et la présence desid
(session id) etsct
(session count). Absents = config trop tard/événement bloqué. - CMP
Refaites le test en refusant puis en acceptant. Sisession_start
n’apparaît qu’après un long délai post-consentement, ajustez le chaînage : CMP en Consent Init, GA4 en Initialization conditionnée. - Bloqueurs
Test A/B : navigateur “propre” vs navigateur avec uBlock/Brave. Sisession_start
disparaît, migrez vers server-side + sous-domaine first-party et renseigneztransport_url
. - SPA
Naviguez sur trois routes. Vous devez voir un seulsession_start
(au début), puis despage_view
à chaque route. Pas desession_start
? Votre première vue n’est pas correctement initialisée.
Bonnes pratiques qui évitent 80 % des cas
- Config GA4 sur Initialization, events ensuite, tag sequencing activé.
- Consent mode maîtrisé : n’attendez pas 2 s après le clic pour lancer GA4, et ne déclenchez pas d’events avant consentement accordé si vous voulez des sessions exploitables.
- Server-side :
server_container_url
partout, endpoint HTTPS first-party,transport_url
côté web. - MP discipliné : toujours client_id réel + session_id numérique réel, sinon vos hits vivent hors session.
- Évitez les audience triggers pour générer des events (asynchrones = hors session).
- Analysez hors 48 h pour juger du vrai taux (les données fraîches sont parfois incomplètes).
Indices concrets d’un session_start
absent
- Acquisition : hausse de (not set) sur des événements pourtant “on-site”.
- DebugView : suites d’événements sans le drapeau
session_start
au début. - Network : pas de requête
en=session_start
au premier hit,sid
manquant sur les suivants.
Deux aides de terrain
- Récupérer le client_id côté web (utile pour MP aligné) : htmlCopyEdit
<script> // nécessite gtag déjà chargé gtag('get', 'G-XXXXXXX', 'client_id', (cid) => { console.log('client_id', cid); }); </script>
- Vérifier le session_id sans dev : Ouvrez DevTools → Application/Stockage → Cookies →
_ga_<MEASUREMENT_ID>
: cherchezsid
(numérique). S’il n’apparaît jamais à la première page, la config ne s’exécute pas assez tôt… ou pas du tout.
Aphorisme pour route : “Une session sans session_start
, c’est un alibi sans témoin.” Assurez l’ordre, respectez le consentement, domestiquez les bloqueurs, et session_start
revient sagement prendre sa place en tête de vos événements.
Hypothèse 5 : Audience triggers : le faux bon plan
Au départ, l’idée paraissait élégante : déclencher automatiquement un événement quand un visiteur entre dans une audience. Pas de condition tarabiscotée dans GTM, pas de code côté site, juste un “si l’utilisateur correspond à X, GA4 émet l’événement Y”. Sur le papier, c’est du tracking sans friction. Dans la vraie vie, c’est un lance-patate qui tire sur votre attribution.
Pourquoi ça semblait malin
- Centralisation : on définit la logique une fois (dans Admin > Audiences), et on la réutilise partout.
- Granularité : on peut combiner des conditions complexes (fréquence, valeur, séquences) sans toucher au front.
- Zéro dev : promesse irrésistible pour les équipes déjà surchargées.
Pourquoi ça flingue l’attribution
- Événements détachés de toute session : l’évaluation d’une audience n’est pas synchrone avec l’action de l’utilisateur. Elle peut se produire après coup (quelques minutes… ou plus). Résultat : l’“audience trigger” tombe hors du cadre de la session réelle. Sans session active, pas de source / support qui tienne. Bonjour “(not set)”.
- Contexte absent : ces événements n’arrivent pas à la suite d’un page_view ou d’un vrai hit utilisateur. Ils sont “générés” par GA4, sans les paramètres de contexte habituels (chemin, campagne, etc.). Vous récoltez un événement, mais vous perdez le pourquoi et le d’où.
- Bruits en acquisition et en conversions : si vous marquez ces événements comme conversions, vous créez des conversions non attribuables. Ça gonfle le compteur, ça vide le sens. Pire, ça peut fausser vos modèles d’enchères si vous les exportez vers Ads.
- Temporalité bancale : l’entrée dans l’audience peut se produire une fois la session terminée (ou à la session suivante). Vous pensez “conversion immédiate”, vous mesurez en fait un statut d’utilisateur arrivé en retard.
Aphorisme utile : “Si ce n’est pas déclenché par l’utilisateur, ce n’est pas un événement, c’est une notification.” Les notifications, GA4 sait les stocker, mais il ne sait pas leur inventer une source.
Les symptômes sur vos rapports
- Une poignée d’événements au nom souvent “propre” (ex.
au_entered_lead_audience
) avec (not set) en Source/Support. - Des conversions qui montent, mais aucune campagne ne peut les revendiquer sans se racler la gorge.
- Des écarts étranges entre explorations (centrées comportement) et rapports d’acquisition (centrés attribution).
La solution pragmatique
- Supprimer les audience triggers pour la mesure. Gardez les audiences pour le remarketing et la segmentation, pas pour émettre des événements.
- Remplacer par de vrais événements côté site / GTM : déclenchez
generate_lead
,purchase
,sign_up
… au moment où l’action se produit, avec le contexte de session intact. - Si vous avez absolument besoin d’un “entrée dans l’audience” :
- Envoyez-le côté client au moment où la condition devient vraie (ex. après le 3e achat, après N visites), pas via Audience Trigger.
- Ou n’en faites pas un événement : stockez un user_property (ex.
is_high_value = true
) et servez-vous-en dans l’analyse et l’activation.
- Désactivez la case “Marquer comme conversion” pour tout événement qui viendrait d’un audience trigger hérité. Une conversion doit naître d’une action, pas d’un recalcul asynchrone.
Procédure d’assainissement en 10 minutes
- Audit : Admin > Audiences → ouvrez chaque audience → cherchez la section Audience trigger.
- Inventaire : listez les événements auto-déclenchés (préfixez-les
au_
si ce n’est pas déjà le cas, pour les repérer). - Suppression : désactivez/supprimez le trigger dans chaque audience.
- Conversions : Admin > Conversions → décochez celles qui correspondent à des
au_*
. - Remplacement : implémentez l’événement équivalent dans GTM/gtag avec un déclencheur basé sur l’action réelle.
- Vérification : DebugView → contrôlez que l’événement part dans la même session que l’action.
- Surveillance : Rapport d’acquisition → filtrez sur l’événement remplacé →
not set
doit chuter vers zéro (hors bruit de fond inhérent).
Alternatives propres selon le besoin
- Scoring / statut utilisateur : user_properties + BigQuery (pas d’événement artificiel).
- Cross-device / back-office : Measurement Protocol, mais avec client_id + session_id d’une session active si vous voulez de l’attribution ; sinon, assumez que c’est hors session et n’en faites pas une “conversion”.
- Campagnes : gardez les conversions sur les événements natifs (clic, envoi, paiement). Attribuez les statuts d’audience au CRM, pas au moteur d’attribution.
Résumé sec : les audience triggers étaient une bonne idée pour marketer pressé, pas pour mesure propre. Ils génèrent des événements qui arrivent en retard, sans session, donc sans attribution. Remplacez-les par des déclenchements côté site, gardez les audiences pour cibler, et votre “not set” s’évaporera aussi vite que la patience d’un analyste devant un funnel en pointillés.
Hypothèse 6 : Paramètres utm incorrects
Les UTM sont la ceinture de sécurité de l’attribution. Mal bouclées, vous traversez le pare-brise et atterrissez en (not set). Rappel de base : utm_source
, utm_medium
, utm_campaign
sont obligatoires. Le reste (utm_term
, utm_content
, utm_id
) est utile, mais jamais un substitut.
Que se passe-t-il quand ils sont manquants ou incohérents ?
utm_source
absent → GA4 ne sait pas “qui” envoie le trafic. La dimension Source/Support bascule en (not set) ou se rabat sur “referral/direct” selon le contexte.utm_medium
absent ou exotique → pas de mappage correct vers les Default Channel Groups. Vos clics “payants” finissent en “Unassigned”, votre reporting devient un cubisme douteux.utm_campaign
manquant → la campagne n’existe pas dans les rapports. Utile si vous aimez jeter de l’argent sans le retrouver.- Typos (
utm-sorce
,utm_meduim
) → GA4 ne corrige pas votre orthographe. C’est ignoré, point. - Casse et variations (
Email
vsemail
,Facebook
vsfacebook
) → fragmentation des rapports, impossible d’additionner proprement. - UTM sur des liens internes → vous vous ré-attribuez la session au milieu du parcours. Bonjour les auto-référents, au revoir l’attribution initiale.
- Redirections qui suppriment la query string → vous posez des UTM… que le serveur jette à la poubelle. Résultat : (not set).
Conventions simples qui évitent 90 % des dégâts
- Toujours les 3 :
utm_source
,utm_medium
,utm_campaign
. - Minuscules partout (source, medium, campaign).
- Mediums “canoniques” (alignez-les sur les Channel Groups par défaut de GA4) :
cpc
ouppc
→ Paid Searchpaid_social
→ Paid Socialsocial
→ Organic Social (selon source)email
→ Emaildisplay
→ Displayaffiliate
→ Affiliatesreferral
→ Referral
- Sources claires :
google
,bing
,facebook
,instagram
,linkedin
,newsletter_nom
,partner_nom
. - Campagne stable :
campagne-printemps-2025
(évitez espaces/accents ; tirets courts). - N’utilisez jamais d’UTM en interne (navigation, boutons, bannière maison).
- Conservez les UTM à travers les redirections (sinon, vous mesurez… le néant).
Exemples concrets
Correct (email)
https://exemple.com/offre?utm_source=newsletter&utm_medium=email&utm_campaign=lancement-produit
Correct (paid social)
https://exemple.com/offre?utm_source=facebook&utm_medium=paid_social&utm_campaign=retargeting-q3
À éviter (medium vague)
...utm_medium=campaign // pas mappé → “Unassigned”
À proscrire (lien interne)
/panier?utm_source=menu&utm_medium=internal&utm_campaign=nav // casse l’attribution
Bonus utiles mais optionnels
utm_id
: identifiant de campagne stable (alimentation de la dimension ID de campagne). Pratique pour regrouper variantes créatives sous une seule campagne.utm_content
: différencier A/B créa, emplacements, formats.utm_term
: mots-clés pour Paid Search hors auto-tagging.
Audit UTM : méthode express, résultat propre
- Inventaire des sources de liens
Ads (Google, Meta, LinkedIn), newsletters, CRM, partenaires/affiliation, SMS, push, influence, QR codes, PDF, signatures email. - Extraction des URLs
- Depuis vos plateformes (exports),
- Depuis GA4 via l’exploration “Landing page + requête” pour voir les UTM réellement utilisés à l’entrée.
- Contrôle de présence et d’orthographe
- Cherchez
utm_source=
,utm_medium=
,utm_campaign=
(exact, pas d’underscore/typo). - Repérez les casse incohérentes (
Email
vsemail
).
- Cherchez
- Validation des mediums
- Comparez vos valeurs à la liste “canonique” ci-dessus.
- Créez si besoin un Custom Channel Group dans GA4 pour mapper proprement des mediums maison, mais soyez constants.
- Test des redirections
- Cliquez sur chaque URL taguée → la page finale doit conserver les UTM.
- Si vous perdez les paramètres, corrigez le serveur :
- Nginx :
rewrite ^/old$ /new?$args permanent;
- Apache :
RewriteRule ^old$ /new [R=301,L,QSA]
- Nginx :
- Sanctions
- Supprimez tous les UTM internes.
- Normalisez par lot : remplacez
Email
→email
,Paid Social
→paid_social
, etc.
- Gouvernance
- Rédigez une nomenclature d’une page avec exemples approuvés.
- Fournissez un générateur UTM (Google Sheet / petit outil) qui impose minuscules et champs obligatoires.
Mini check-list “prêt à publier”
- Les 3 UTM obligatoires sont présents.
utm_medium
appartient à la liste validée.- Valeurs en minuscules, sans espaces, sans accents.
- L’URL finale conserve la query string après redirection.
- Aucun UTM utilisé sur des liens internes.
utm_id
appliqué si vous avez des variantes créatives nombreuses.
Détection des dégâts dans ga4 (pour agir vite)
- Acquisition > Trafic : filtrez par Session source/medium = (not set) et inspectez les Pages de destination correspondantes.
- Explorations : tableau avec Session source/medium, Session campaign, Landing page + requête. Les lignes avec
?
vide côté requête mais campagne renseignée = UTM perdus en route. - Tendance : si
(not set)
explose le jour même d’un envoi newsletter ou d’une campagne, vos UTM sont mal formés ou écrasés par une redirection.
Erreurs → symptômes → correctifs (mémo)
Erreur | Symptôme dans GA4 | Correctif |
---|---|---|
utm_source manquant | (not set) en Source/Support | Ajouter utm_source (valeur claire, minuscule) |
utm_medium non standard | Canal = Unassigned | Remapper sur cpc , email , paid_social , display , etc. |
utm_campaign absent | Campagnes vides | Renseigner un nom stable (sans espaces) |
Typos (utm-sorce ) | UTM ignorés | Corriger à la source, outiller la saisie |
UTM internes | Auto-réattribution, self-referrals | Supprimer, utiliser events/params internes |
Redirection qui coupe la query | (not set) malgré UTM posés | Conserver $args / QSA dans la règle serveur |
Aphorisme pour conclure : “Une UTM mal fichue, c’est un alibi en Comic Sans.” Faites simple, faites constant, testez les redirections, et votre attribution cessera de jouer à cache-cache avec (not set).
Hypothèse 7 : Données récentes non traitées
Analyser aujourd’hui et hier dans GA4, c’est discuter avec un déménageur en plein carton : la moitié des infos n’est pas rangée. Les rapports standards ne sont pas en temps réel. La pipeline “intraday” remonte une partie des événements pendant la journée, puis la pipeline “daily” complète et corrige… jusqu’à 24–48 h après. Entre-temps, il y a des trous temporaires dans les dimensions d’attribution event-scoped (source, medium, campagne, channel). Traduction opérationnelle : ce qui n’est pas encore rattaché tombe plus souvent en (not set), puis “revient” quand le traitement quotidien passe. support.google.com+1
Conséquence directe : le “not set” gonfle artificiellement sur les fenêtres < 48 h. Quand les données “daily” arrivent, GA4 rattache mieux les événements à la bonne source/support et applique votre modèle d’attribution. Et ce n’est pas qu’un rattrapage : le crédit d’attribution de vos conversions peut évoluer jusqu’à 12 jours après l’événement, le temps que la modélisation finisse son boulot. Moralité : ne jugez jamais une campagne sur un jour ou deux, encore moins le lendemain matin. support.google.com
Côté BigQuery Export, même logique :
- Les tables daily (
events_YYYYMMDD
) peuvent être ré-écrites jusqu’à 72 h pour intégrer des hits en retard (Measurement Protocol, SDKs). - Les tables intraday donnent de la fraîcheur, mais restent “provisoires”.
Si vous faites vos dashboards sur BQ, gardez un lag d’au moins 48–72 h avant d’appeler un jour “figé”. support.google.com
Pourquoi cela fabrique du (not set) ?
- Les trafics sources au niveau événement arrivent parfois après coup ; tant qu’ils ne sont pas là, l’événement est compté mais sans attributions complètes. 2) Les pipelines intraday appliquent des règles plus strictes (cardinalité, agrégation) en attendant la “daily”, ce qui multiplie les “valeurs par défaut” et les cases vides. Résultat : l’acquisition se nettoie en différé. support.google.com
Ligne de conduite pragmatique
- Exclure systématiquement J et J-1 de vos analyses d’acquisition. Focalisez-vous sur J-2 → J-31 pour la décision.
- Pour les tableaux de bord, imposez un décalage fixe (ex. “derniers 7 jours hors 2 derniers jours”).
- Si vous devez monitorer à chaud, utilisez Temps réel et DebugView pour la santé du tracking, mais jamais pour l’attribution. Les vrais rapports, c’est quand la “daily” est passée. support.google.com
Mémo express
Fenêtre analysée | Ce qui se passe | Risque sur “not set” | Recommandation |
---|---|---|---|
Aujourd’hui / Hier | Données intraday partielles, trous temporaires dans source/medium | Élevé | Ne pas conclure, monitorer seulement |
J-2 → J-7 | Données daily, attribution plus stable (peut encore bouger) | Faible à modéré | OK pour reporting hebdo |
J-8 → J-14 | Données stables, modélisation quasiment terminée | Faible | OK pour reporting mensuel |
BigQuery intraday | Frais, incomplet | Élevé | Utiliser pour QA/ops, pas pour décision |
BigQuery daily (≥ 72 h) | Consolidé | Faible | Base fiable pour analyses fines |
Aphorisme utile : « La fraîcheur des données, c’est comme le fromage : trop frais, ça pique les yeux. » Laissez 48 h à GA4 pour lier correctement les événements à leurs sources/supports. Vous verrez le (not set) reculer tout seul, sans exorcisme, juste avec du temps de traitement.
Hypothèse 8 : Trafic spam/bots
Le (not set) peut exploser sans prévenir quand des bots arrosent vos endpoints. Ils ne portent pas d’UTM, ne déclenchent pas toujours session_start
, et inondent vos rapports d’événements hors attribution. GA4 filtre déjà une partie des “bots connus” (IAB), mais ce filet a des mailles larges : ce qui n’est pas sur la liste passe. support.google.com
Signes d’alerte (simples, efficaces)
- Pics soudains d’événements/sessions, souvent sur des heures creuses, sans campagne en cours.
- “(not set)” massif dans Acquisition sur des événements pourtant “on-site”.
- Pages de destination bizarres (URLs techniques, 404, endpoints d’API, pages cachées).
- Pays/langues improbables, résolutions d’écran impossibles, user agents exotiques.
- Self-referrals ou rafales sur la même route (ex :
/cart
) sans cheminement normal.
Mesures qui marchent (du plus proactif au plus réactif)
1) Activer une protection anti-bot au niveau CDN (Cloudflare & co.)
Objectif : bloquer/challenger les requêtes avant qu’elles n’atteignent votre site (et donc GA4).
- Cloudflare attribue un bot score (1–99) à chaque requête ; scores bas = requêtes probablement automatisées. Les modèles par défaut permettent d’autoriser les bots vérifiés (Googlebot) et de challenger ou bloquer les autres selon un seuil, par exemple “< 30 = bot probable”. Cloudflare Docs+2Cloudflare Docs+2
- Concrètement, créez une règle WAF : scssCopyEdit
if (cf.bot_management.score < 30) then Challenge or Block
L’intérêt : vous nettoyez à la source, sans polluer GA4, tout en laissant passer les bons robots (SEO). Cloudflare Docs+1
Effet attendu : chute immédiate des pics, baisse du (not set) corrélée aux heures/segments ciblés par les règles.
2) Filtrer côté server-side tagging avec un “bot score”
Si vous utilisez GTM serveur, ajoutez une couche de tri avant d’émettre les hits GA4.
- Hébergeurs sGTM comme Stape exposent des en-têtes type
X-Device-Bot
etX-Device-Bot-Score
(1–100). Créez une Variable “Request Header” dans sGTM et n’exécutez pas vos tags si le score dépasse votre seuil (ex. > 90). stape.io+1
Pseudo-logique de déclencheur (sGTM) :
IF RequestHeader.X-Device-Bot == 'true' OR RequestHeader.X-Device-Bot-Score >= 90
THEN Do not fire GA4 tag
Intérêt : vous évitez d’enregistrer l’événement côté GA4 tout en gardant la mesure côté serveur pour l’analyse forensique.
3) Filtrage IP dans GA4 (solution réactive)
Quand un lot d’IP vous attaque, isolez-le :
- Admin → Flux de données → Configurer les paramètres des tags → Définir le trafic interne : créez une règle “bots” et listez les IP (ou une regex).
- Admin → Filtres de données → activez Internal traffic en Exclude.
Méthode imparfaite (les IP changent), mais utile pour couper l’hémorragie pendant que le CDN/WAF se met en place. support.google.com
Workflow d’enquête (10 minutes, montre en main)
- Confirmer le bot burst
Acquisition > Trafic → filtrez Session source/medium = (not set) → croisez avec Page de destination et Pays/UA. Si tout pointe vers la même route, c’est rarement humain. - Inspecter le réseau
Logs serveur ou CDN : mêmes IPs / ASNs / UA qui spamment ? Notez-les. - Mitiger au périmètre
Règle Cloudflare basée sur bot score + pays/ASNs suspect → Challenge/Block. Cloudflare Docs - Durcir la collecte
sGTM : ajoutez la condition “bot score” sur les triggers GA4. stape.io - Purger le bruit rémanent
GA4 : règle Internal traffic = bots + filtre Exclude (temporaire mais utile). support.google.com - Vérifier le retour à la normale
Suivez la part de (not set) par heure/jour (hors 48 h, traitement oblige). Si ça ne baisse pas, haussez le niveau (captcha, règles plus strictes). support.google.com
mémo décisions → effets attendus
Action | Où | Effet sur bot | Effet sur “(not set)” |
---|---|---|---|
Règle bot score < 30 → challenge | CDN (Cloudflare) | Stoppe à la porte | Forte baisse immédiate |
X-Device-Bot-Score ≥ 90 → ne pas tirer GA4 | sGTM | Évite l’enregistrement | Baisse nette, surtout sur pics |
Filtre IP “bots” | GA4 | Coupe une vague ciblée | Baisse partielle (réactive) |
Laisser faire “IAB bots filtering” | GA4 (auto) | Filtre les connus | Insuffisant contre bots récents |
Rappel utile : GA4 exclut déjà automatiquement une partie du trafic bot/spider basé sur des listes “connues”. Si malgré tout (not set) grimpe, c’est au-delà de ces listes — il faut agir en amont (CDN/WAF) et au niveau collecte (sGTM). support.google.com
Deux garde-fous supplémentaires
- Cachez vos endpoints : utilisez un sous-domaine first-party pour GA4 (server-side) et un
transport_url
pour éviter le repérage trivial degoogle-analytics.com
. - Bloquez les référents pourris : ajoutez vos domaines techniques à la liste des références non désirées pour éviter les auto-référents en cascade.
Aphorisme pour finir : « Contre les bots, la meilleure attribution, c’est la porte d’entrée fermée. » Mettez le WAF devant, le score au milieu, et le filtre derrière. Votre (not set) retrouvera sa taille de bruit de fond.
Conclusion
“(not set)” n’est pas une fatalité. C’est un symptôme. Quand la source/support disparaît, ce n’est pas l’univers qui complote : c’est une chaîne de détails techniques mal ordonnés. Mauvais ordre des balises, server-side percé, Measurement Protocol fantaisiste, session_start
absent, audience triggers asynchrones, UTM bancales, données trop fraîches, bots en goguette. Additionnez tout ça et vos rapports ressemblent à un roman noir sans coupable.
L’approche “rustines” (un correctif par-ci par-là) ne marche pas. Elle déplace le problème. Ce qu’il faut, c’est un audit complet, court, précis, impitoyable avec les incohérences.
Audit express (priorisé, exécutable)
- Fenêtre d’analyse
- Écarter J et J-1. Travailler sur J-2 → J-31 pour juger du taux réel de (not set).
- Objectif : éviter les faux “trous” d’ingestion.
- Ordre de tir (GTM Web / gtag)
- Config GA4 en Initialization, events après (sequencing activé).
- Objectif :
session_start
avant tout event. Taux desession_start
manquants ≤ 2 %.
- Server-side homogène
server_container_url
partout (config et events).transport_url
si gtag hard-codé.- Objectif : 100 % des hits via votre endpoint, zéro requête directe
google-analytics.com
.
- Measurement Protocol discipliné
- Réutiliser client_id et session_id réels de la session web active (+
timestamp_micros
cohérent,engagement_time_msec ≥ 1
). - Objectif : 0 événement MP “hors session” marqué conversion.
- Réutiliser client_id et session_id réels de la session web active (+
- Consent & bloqueurs
- CMP en Consent Initialization, GA4 déclenché en connaissance de cause.
- Envisager server-side sur sous-domaine first-party pour limiter le blocage.
- Objectif : sessions stables malgré privacy tools.
- SPA & navigation
- Première charge : config + (éventuel)
page_view
. - Routes :
page_view
manuel avecpage_location
/page_referrer
. - Objectif : un seul
session_start
au début, des vues nettes ensuite.
- Première charge : config + (éventuel)
- UTM & canaux
- Imposer les 3 obligatoires (
utm_source
,utm_medium
,utm_campaign
), minuscules, mediums canoniques. - Interdire les UTM internes.
- Objectif : 0 “Unassigned”, 0 typos, 0 UTM perdus en redirection.
- Imposer les 3 obligatoires (
- Audience triggers
- Supprimer les déclencheurs d’audience qui émettent des events.
- Conserver les audiences pour la segmentation / activation, pas pour “fabriquer” des hits.
- Objectif : plus d’événements asynchrones sans session.
- Anti-bot en amont
- WAF/CDN (Cloudflare…) avec bot score → challenge/block.
- En sGTM : ignorer les hits au-delà d’un seuil de score bot.
- En GA4 : filtre IP (solution réactive).
- Objectif : faire chuter les pics et la part de (not set) corrélée.
- Contrôles & seuils
- DebugView : ordre
session_start → page_view → events
. - Network : premier hit =
en=session_start
avecsid
/sct
. - KPI de propreté : (not set) ≤ 1–2 % hors 48 h, stable par canal.
Mémo “cause → symptôme → action”
Cause | Symptôme | Action nette |
---|---|---|
Event avant config | Events sans sid , (not set) | Config en Initialization + sequencing |
server_container_url absent | Hits mixtes, sessions dupliquées | URL serveur partout + transport_url |
MP sans IDs réels | Conversions hors attribution | Passer client_id /session_id web au backend |
session_start manquant | Suites d’événements orphelins | Optimiser ordre/consent, viser ≤ 2 % manquants |
Audience triggers | Events (not set) asynchrones | Supprimer, remplacer par vrais events |
UTM foireux | “Unassigned”, campagnes vides | Normaliser sources/mediums/campagnes |
Données trop fraîches | (not set) gonflé sur J/J-1 | Exclure 48 h des analyses |
Bots | Pics + (not set) massif | WAF/CDN + score bot sGTM + filtres IP |
Livrables attendus (pour clore proprement)
- Nomenclature UTM d’une page, générateur imposant les champs obligatoires.
- Playbook GTM/gtag (ordre, sequencing, Consent Mode, SPA).
- Checklist server-side (endpoint, cookies, exclusions de référents).
- Recette MP (schéma d’IDs, journalisation, tests DebugView).
- Règles WAF/CDN et conditions sGTM anti-bot.
- Tableau de suivi du taux de (not set) par jour/canal (hors 48 h).