Skip to main content

Server side tracking checker

In  questa pagina puoi trovare un codice JS utile per controllare la presenza di una installazione server side.
È da incollare nella console del browser dopo che hai caricato una pagina web.
Lo script restituirà in console il risultato del controllo sotto la voce “verdetto”.



(() => {
  // --------- Utility
  const now = new Date().toISOString();
  const url = new URL(location.href);

  // Grezza eTLD+1: sufficiente per molti casi (acme.it, example.co.uk, ecc.)
  const getETLD1 = (host) => {
    const parts = host.split('.').filter(Boolean);
    if (parts.length <= 2) return host; const two = parts.slice(-2).join('.'); const three = parts.slice(-3).join('.'); const common2lvl = /(\.co\.uk|\.ac\.uk|\.gov\.uk|\.com\.au|\.com\.br|\.com\.mx|\.com\.ar|\.com\.sg|\.com\.tr|\.com\.cn)$/i; return common2lvl.test(three) ? three : two; }; const baseDomain = getETLD1(url.hostname); const isFirstParty = (u) => {
    try { return new URL(u, location.href).hostname.endsWith(baseDomain); }
    catch { return false; }
  };

  const getHostname = (u) => {
    try { return new URL(u, location.href).hostname || ''; }
    catch { return ''; }
  };

  // Pattern noti per “proxy/GTM-SS endpoints”
  const FIRST_PARTY_COLLECT_PATTERNS = [
    /\/g\/collect/i,         // GA4 proxata/SS
    /\/collect(\?|$)/i,      // GA collect generico
    /\/r\/collect/i,         // GA legacy
    /\/j\/collect/i,         // GA legacy
    /\/analytics\/collect/i, // varianti custom
    /\/capi/i,               // endpoint custom per Meta CAPI
    /\/event(s)?\/collect/i, // varianti custom
  ];

  // Vendor terze parti
  const GA_THIRD  = /(^|\.)google-analytics\.com$/i;
  const GTM_THIRD = /(^|\.)googletagmanager\.com$/i;
  const FB_THIRD  = /(^|\.)facebook\.com$|(^|\.)connect\.facebook\.net$/i;

  // Indicatori di "loader proxato"
  const PROXIED_LOADER_PATTERNS = [
    /\/gtm\.js\?id=GTM-/i,   // gtm.js servito in first-party
    /\/tag\/js\/g\?id=G-/i,  // gtag.js servito in first-party
  ];

  // --------- Network-based evidences (Performance API)
  const entries = performance.getEntriesByType('resource')
    .map(e => e.name)
    .filter(Boolean);

  const urls = [...new Set(entries)];

  const parsed = urls.map(u => {
    try {
      const p = new URL(u);
      return { url: u, host: p.hostname, path: p.pathname + (p.search || '') };
    } catch { return { url: u, host: '', path: '' }; }
  });

  const firstPartyCollect = parsed.filter(r =>
    isFirstParty(r.url) && FIRST_PARTY_COLLECT_PATTERNS.some(rx => rx.test(r.path))
  );

  const proxiedLoaders = parsed.filter(r =>
    isFirstParty(r.url) && PROXIED_LOADER_PATTERNS.some(rx => rx.test(r.path))
  );

  const gaThirdCollect = parsed.filter(r =>
    /\/(g|j|r)?\/collect/i.test(r.path) && GA_THIRD.test(r.host)
  );

  const gtmThirdLoader = parsed.filter(r =>
    /\/gtm\.js/i.test(r.path) && GTM_THIRD.test(r.host)
  );

  const fbThirdPixel = parsed.filter(r =>
    /\/fbevents\.js|\/tr\/?/i.test(r.path) && FB_THIRD.test(r.host)
  );

  const gaLoaderPresent = parsed.some(r =>
    /gtag\/js|\/tag\/js\/g\?id=G-/i.test(r.path) || /G-[A-Z0-9]{6,}/.test(r.url)
  );

  // --------- NEW: DOM-based scan for GTM loaders (script + noscript iframe)
  const domScripts = Array.from(document.querySelectorAll('script[src]'));
  const domIframes = Array.from(document.querySelectorAll('iframe[src]'));

  // Individua script GTM (gtm.js) e iframe noscript (ns.html?id=GTM-XXXX)
  const domGtmScripts = domScripts
    .filter(s => /(^|\/)gtm\.js(\?|$)/i.test(s.getAttribute('src') || ''))
    .map(s => {
      const src = s.getAttribute('src') || '';
      const host = getHostname(src);
      return {
        tipo: 'script',
        src,
        host,
        posizione: s.closest('head') ? 'head' : 'body'
      };
    });

  const domGtmIframes = domIframes
    .filter(f => /\/ns\.html\?id=GTM-/i.test(f.getAttribute('src') || ''))
    .map(f => {
      const src = f.getAttribute('src') || '';
      const host = getHostname(src);
      return {
        tipo: 'iframe',
        src,
        host,
        posizione: f.closest('body') ? 'body' : 'altro'
      };
    });

  const domGtmAll = [...domGtmScripts, ...domGtmIframes];

  const domOfficial = domGtmAll.filter(x => GTM_THIRD.test(x.host));
  const domNonOfficial = domGtmAll.filter(x => x.host && !GTM_THIRD.test(x.host));

  // --------- Heuristics / punteggio semplice
  let score = 0;
  if (firstPartyCollect.length) score += 2;                         // forte indizio SS/proxy
  if (proxiedLoaders.length)   score += 1;                         // loader proxati
  if (!gaThirdCollect.length && gtmThirdLoader.length) score += 1; // GA presente ma collect non diretto
  if (gaLoaderPresent && !gaThirdCollect.length && !firstPartyCollect.length) score += 1;

  // NUOVO segnale: GTM caricato da host diverso da googletagmanager.com
  if (domNonOfficial.length) score += 2;

  const verdict =
    score >= 3 ? 'PROBABILE tracking server-side/proxy su first-party'
    : score === 2 ? 'POSSIBILE tracking server-side/proxy'
    : 'IMPROBABILE (dai segnali disponibili)';

  // --------- Output
  console.group('🔎 Server-side/Proxy Tracking Check');
  console.log('Data/ora:', now);
  console.log('Dominio base rilevato (eTLD+1):', baseDomain);

  console.group('Riepilogo segnali');
  console.table([
    { metrica: 'First-party collect endpoints (network)', riscontri: firstPartyCollect.length },
    { metrica: 'Loader proxati (gtm.js/gtag.js in first-party, network)', riscontri: proxiedLoaders.length },
    { metrica: 'Collect GA verso google-analytics.com (network)', riscontri: gaThirdCollect.length },
    { metrica: 'Loader GTM da googletagmanager.com (network)', riscontri: gtmThirdLoader.length },
    { metrica: 'Pixel/JS Facebook terza parte (network)', riscontri: fbThirdPixel.length },
    { metrica: 'GTM ufficiale (DOM: host = googletagmanager.com)', riscontri: domOfficial.length },
    { metrica: 'GTM NON ufficiale (DOM: host ≠ googletagmanager.com)', riscontri: domNonOfficial.length }
  ]);
  console.groupEnd();

  console.log('➤ Verdict:', verdict);

  if (domGtmAll.length) {
    console.group('Dettagli GTM trovati nel DOM');
    console.table(domGtmAll.map(x => ({
      tipo: x.tipo,
      host: x.host || '(relativo / non risolvibile)',
      posizione: x.posizione,
      src: x.src
    })));
    if (domNonOfficial.length) {
      console.warn('⚠️ GTM caricato da host non ufficiale -> indizio di proxy/Server-Side (es. sGTM). Verificare configurazione.');
    }
    console.groupEnd();
  } else {
    console.info('ℹ️ Nessun riferimento GTM trovato nella DOM (script gtm.js / iframe ns.html).');
  }

  if (firstPartyCollect.length) {
    console.group('Dettagli first-party collect endpoints sospetti (network)');
    console.table(firstPartyCollect.map(x => ({ host: x.host, path: x.path })));
    console.groupEnd();
  }
  if (proxiedLoaders.length) {
    console.group('Dettagli loader proxati (gtm.js/gtag.js in first-party, network)');
    console.table(proxiedLoaders.map(x => ({ host: x.host, path: x.path })));
    console.groupEnd();
  }
  if (!firstPartyCollect.length && !domNonOfficial.length && score <= 2) {
    console.info('ℹ️ Nota: l’assenza di collect “visibili” o di loader GTM non ufficiali non esclude un modello server-to-server puro (invisibile dal browser).');
  }

  // Suggerimento operativo leggibile
  if (domNonOfficial.length) {
    console.log('→ Azione consigliata: ispezionare l’host indicato (es. /ns.html o /gtm.js first-party) e verificare presenza di un container sGTM o di un reverse proxy.');
  } else if (firstPartyCollect.length || proxiedLoaders.length) {
    console.log('→ Azione consigliata: verificare regole proxy/rewriting e configurazioni di server-side tagging per GA/Meta.');
  } else {
    console.log('→ Azione consigliata: nessun indizio forte. Controllare comunque lato server/cordate CDN se si sospetta traffico SS.');
  }

  console.groupEnd();
})();