Il tracciamento delle conversioni da form è uno degli aspetti più critici di qualsiasi strategia di web analytics. Un form non tracciato correttamente significa dati persi, attribuzioni sbagliate e campagne ottimizzate su informazioni incomplete.

Ma c’è un aspetto ancora più importante: tracciare la conversione non basta. Se vuoi sfruttare le Enhanced Conversions di Google Ads (conversioni avanzate), devi catturare i dati dell’utente nel momento esatto in cui compila il form — in particolare email, oppure altri dati utili al matching come telefono o indirizzo. Tracciare solo la thank-you page significa perdere queste informazioni per sempre.

Questo articolo è diviso in due parti:

  • Parte 1 — Teoria e Debug: come analizzare qualsiasi form, identificare il tipo di invio e usare la console per capire esattamente cosa succede
  • Parte 2 — Implementazione: script completi, configurazione GTM, trigger, variabili e tag pronti per la produzione

Se vuoi ottenere questo risultato senza sporcarti le mani con debug, dataLayer, trigger e mapping delle variabili, contattami: posso occuparmi io dell’implementazione tecnica e della verifica del tracciamento.


Parte 1: Teoria e Debug

I tre scenari di invio form

Non tutti i form si comportano allo stesso modo. Esistono tre scenari principali:

1. Form con redirect (POST tradizionale)

Il form invia i dati con un POST standard e reindirizza a una thank-you page. È lo scenario classico, ma presenta un problema critico: quando l’utente arriva sulla thank-you page, il form non esiste più. Non puoi quindi recuperare email, nome, telefono o altri dati utili per le Enhanced Conversions.

Come riconoscerlo: la pagina si ricarica completamente dopo il submit e l’URL cambia.

Soluzione: devi intercettare il submit prima del redirect, catturare i dati e solo dopo lasciare proseguire l’invio.

2. Form AJAX con evento submit

Il form invia i dati via JavaScript senza ricaricare la pagina. L’evento submit viene comunque triggerato, quindi puoi intercettarlo con un event listener.

Come riconoscerlo: la pagina non si ricarica, ma l’evento submit è presente e verificabile in console.

Soluzione: event listener su submit, con preventDefault() se necessario.

3. Form AJAX senza evento submit (terze parti)

Il form bypassa completamente il submit nativo. Cliccando il pulsante, viene eseguita una funzione JavaScript che invia i dati tramite XMLHttpRequest o Fetch API. Nessun evento submit viene triggerato.

Come riconoscerlo: il pulsante non è un <button type="submit">, oppure il form ha onsubmit="return false", oppure semplicemente l’evento submit non scatta mai.

Soluzione: intercettazione di XMLHttpRequest o Fetch API.

I form embedded di terze parti come Brevo, HubSpot, Mailchimp, Typeform rientrano quasi sempre in questo terzo scenario.


Come analizzare un form: la fase di debug

Prima di scrivere qualsiasi codice, devi capire esattamente come funziona il form. Apri gli strumenti di sviluppo del browser (F12) e segui questa procedura.

Step 1: Ispeziona la struttura del form

Nella tab Elements, trova l’elemento <form> e annota:

  • ID: es. id="sib-form"
  • Action: URL di destinazione (vuoto = stesso dominio, URL esterno = terze parti)
  • Method: GET o POST
  • Attributi speciali: onsubmit, data-*

Step 2: Mappa i campi del form

Per ogni campo, documenta l’attributo name: è quello che userai per recuperare i valori.

// Esegui in console per elencare tutti i campi del form
var form = document.getElementById('ID_DEL_FORM');
var fields = form.querySelectorAll('input, select, textarea');
fields.forEach(function(f) {
  console.log('name:', f.name, '| type:', f.type, '| value:', f.value);
});

Step 3: Verifica se l’evento submit viene triggerato

Questo è il test cruciale. Esegui in console:

// Test: l'evento submit viene triggerato?
var form = document.getElementById('ID_DEL_FORM');
form.addEventListener('submit', function(e) {
  console.log('✅ Evento SUBMIT triggerato!');
  console.log('Form data:', new FormData(form));
});
// Ora compila il form e clicca submit. Se vedi il messaggio, puoi usare un listener su submit.
// Se NON vedi nulla, il form bypassa il submit nativo.

Step 4: Monitora le chiamate di rete

Apri la tab Network, filtra per Fetch/XHR, poi compila e invia il form. Osserva:

  • URL della richiesta: dominio interno o esterno? (es. sibforms.com)
  • Metodo: POST, GET
  • Payload: clicca sulla richiesta → tab “Payload” per vedere i dati inviati
  • Response: status 200? JSON di risposta?

Step 5: Identifica il tipo di chiamata (XHR o Fetch)

Se il form non triggera submit ma vedi una chiamata in Network, devi capire se usa XMLHttpRequest o Fetch API. Esegui in console prima di inviare il form:

// Intercetta XMLHttpRequest
(function() {
  var origOpen = XMLHttpRequest.prototype.open;
  XMLHttpRequest.prototype.open = function(method, url) {
    console.log('🔵 XHR:', method, url);
    return origOpen.apply(this, arguments);
  };
})();
// Intercetta Fetch API
(function() {
  var origFetch = window.fetch;
  window.fetch = function(url, options) {
    console.log('🟢 FETCH:', url, options);
    return origFetch.apply(this, arguments);
  };
})();

Ora invia il form. In console vedrai:

  • 🔵 XHR: POST https://... → il form usa XMLHttpRequest
  • 🟢 FETCH: https://... → il form usa Fetch API

Questa informazione è fondamentale per scegliere la tecnica di intercettazione corretta.


Riepilogo: quale strategia usare

ScenarioTestStrategia
Pagina si ricarica dopo submitURL cambiaIntercetta submit, cattura dati, poi lascia proseguire
Evento submit triggeratoConsole mostra “✅ Evento SUBMIT”Event listener su submit
Nessun submit, XHR in consoleConsole mostra “🔵 XHR”Override di XMLHttpRequest.prototype
Nessun submit, Fetch in consoleConsole mostra “🟢 FETCH”Override di window.fetch

Parte 2: Implementazione

Ora che sai come analizzare un form, vediamo l’implementazione completa. Useremo come caso pratico un form Brevo (ex Sendinblue), che rappresenta lo scenario più complesso: nessun evento submit, invio tramite XHR a dominio esterno.

Caso pratico: Form Brevo embedded

Dall’analisi del form (seguendo gli step della Parte 1) emergono queste caratteristiche:

  • ID: sib-form
  • Evento submit: NON triggerato
  • Tipo chiamata: XHR a sibforms.com con parametro isAjax=1
  • Campi: NOME, COGNOME, AZIENDA, EMAIL, SMS, MESSAGGIO, CONFERMA
  • Widget custom: selettore prefisso telefonico (classe .sib-sms-select, valore in data-value)

2.1 Script di tracciamento (Tag Custom HTML)

Crea un nuovo tag in GTM di tipo Custom HTML e incolla questo codice:

<script>
(function() {
  // ==================================================
  // CONFIGURAZIONE - Modifica questi valori
  // ==================================================
  var FORM_ID = 'sib-form';                    // ID del form
  var XHR_FILTER = 'sibforms.com';             // Dominio da intercettare
  var XHR_PARAM = 'isAjax=1';                  // Parametro che identifica la chiamata
  var EVENT_NAME = 'brevo_form_submit';        // Nome evento per GTM
  var FORM_NAME = 'contact_request_form';      // Nome descrittivo del form
  
  // ==================================================
  // VERIFICA ESISTENZA FORM
  // ==================================================
  var form = document.getElementById(FORM_ID);
  if (!form) return;
  
  // ==================================================
  // PREVIENI DOPPIA ESECUZIONE
  // ==================================================
  if (window['__formTracker_' + FORM_ID]) return;
  window['__formTracker_' + FORM_ID] = true;
  // ==================================================
  // HELPER FUNCTIONS
  // ==================================================
  
  // Recupera il valore di un campo
  function getFieldValue(name) {
    var el = form.querySelector('[name="' + name + '"]');
    if (!el) return '';
    if (el.type === 'checkbox') return el.checked;
    return el.value || '';
  }
  // Recupera il prefisso internazionale dal widget Brevo
  function getSmsCountryCode() {
    var widget = form.querySelector('.sib-sms-select');
    if (!widget) return '+39';
    return widget.getAttribute('data-value') || '+39';
  }
  // Formatta il numero in formato E.164
  function formatPhoneE164(code, number) {
    if (!number) return '';
    var clean = number.replace(/\s+/g, '').replace(/^0+/, '');
    return code + clean;
  }
  // ==================================================
  // INTERCETTAZIONE XMLHttpRequest
  // ==================================================
  var origOpen = XMLHttpRequest.prototype.open;
  var origSend = XMLHttpRequest.prototype.send;
  XMLHttpRequest.prototype.open = function(method, url) {
    this._url = url;
    this._method = method;
    return origOpen.apply(this, arguments);
  };
  XMLHttpRequest.prototype.send = function(body) {
    var xhr = this;
    
    // Filtra solo le chiamate al servizio del form
    if (xhr._url && 
        xhr._url.indexOf(XHR_FILTER) !== -1 && 
        xhr._url.indexOf(XHR_PARAM) !== -1) {
      
      // Cattura i dati PRIMA dell'invio
      var smsCountryCode = getSmsCountryCode();
      var formData = {
        user_first_name: getFieldValue('NOME'),
        user_last_name: getFieldValue('COGNOME'),
        user_company: getFieldValue('AZIENDA'),
        user_email: getFieldValue('EMAIL'),
        user_phone_country_code: smsCountryCode,
        user_phone: formatPhoneE164(smsCountryCode, getFieldValue('SMS')),
        user_message: getFieldValue('MESSAGGIO'),
        user_consent: getFieldValue('CONFERMA')
      };
      // Ascolta la risposta
      xhr.addEventListener('load', function() {
        // Traccia solo se la richiesta ha avuto successo (status 2xx)
        if (xhr.status >= 200 && xhr.status < 300) {
          window.dataLayer = window.dataLayer || [];
          window.dataLayer.push({
            event: EVENT_NAME,
            form_id: FORM_ID,
            form_name: FORM_NAME,
            user_first_name: formData.user_first_name,
            user_last_name: formData.user_last_name,
            user_company: formData.user_company,
            user_email: formData.user_email,
            user_phone_country_code: formData.user_phone_country_code,
            user_phone: formData.user_phone,
            user_message: formData.user_message,
            user_consent: formData.user_consent
          });
          
          console.log('[GTM] Form tracciato:', EVENT_NAME, formData);
        }
      });
    }
    
    return origSend.apply(this, arguments);
  };
})();
</script>

Trigger per il tag

Imposta il trigger su DOM Ready (o Page View se il form è presente subito). Se il form è solo su alcune pagine, aggiungi una condizione sull’URL.


2.2 Trigger Custom Event

Crea un nuovo Trigger in GTM:

  • Tipo: Custom Event
  • Event name: brevo_form_submit
  • This trigger fires on: All Custom Events

Questo trigger scatterà ogni volta che lo script fa il push al dataLayer con l’evento brevo_form_submit.


2.3 Variabili Data Layer

Per utilizzare i dati del form nei tag, crea queste variabili di tipo Data Layer Variable:

Nome variabile GTMData Layer Variable NameUso
DLV – user_emailuser_emailEnhanced Conversions
DLV – user_first_nameuser_first_nameAddress data / CRM / reportistica
DLV – user_last_nameuser_last_nameAddress data / CRM / reportistica
DLV – user_phoneuser_phoneEnhanced Conversions
DLV – form_nameform_nameAnalytics / report

Nota: il fatto che questi dati siano disponibili nel dataLayer non significa che debbano essere sempre inviati tutti a Google Ads. Nelle Enhanced Conversions il mapping va fatto rispettando i gruppi di dati accettati dalla piattaforma.


2.4 Tag Google Ads Conversion

Crea un tag Google Ads Conversion Tracking:

  • Conversion ID: il tuo ID conversione
  • Conversion Label: la tua etichetta
  • Trigger: il trigger brevo_form_submit creato sopra

Per le Enhanced Conversions, nella sezione “Include user-provided data”, non trattare i campi come combinabili liberamente. Google Ads accetta almeno uno di questi set di dati: email (preferita), oppure indirizzo con first name, last name, postal code e country; il telefono può essere inviato come match key, ma per massimizzare qualità e copertura è consigliato insieme all’email. :contentReference[oaicite:0]{index=0}

  • Email: {{DLV - user_email}} (opzione preferita e sufficiente da sola)
  • Phone: {{DLV - user_phone}} (può essere inviato come chiave di matching; in pratica conviene quasi sempre accompagnarlo all’email)
  • First Name e Last Name: usali solo se stai passando anche i dati di indirizzo richiesti da Google Ads, cioè almeno postal code e country; facoltativamente puoi aggiungere via, città e regione. :contentReference[oaicite:1]{index=1}

In pratica: se il tuo form raccoglie solo email e telefono, configura l’Enhanced Conversion con questi due campi. Se invece vuoi usare anche nome e cognome, devi strutturare il mapping come address data, non come semplici campi anagrafici isolati. :contentReference[oaicite:2]{index=2}


2.5 Mapping corretto per Enhanced Conversions

Lo schema delle variabili utilizzato in questo articolo è compatibile con Google Ads solo se rispetti i gruppi di dati richiesti dalla piattaforma. Per le Enhanced Conversions for web, Google indica come set validi almeno uno tra: email, oppure full name + address con campi minimi obbligatori, oppure phone number come match key. :contentReference[oaicite:3]{index=3}

Variabile dataLayerFormatoQuando usarla in Google Ads
user_emailemail standardPuò essere usata da sola
user_phoneE.164 (es. +393331234567)Può essere usata come chiave di matching; in pratica è consigliato inviarla insieme all’email quando disponibile
user_first_nametestoDa usare solo come parte di address.first_name
user_last_nametestoDa usare solo come parte di address.last_name
user_postal_codeCAPNecessario se vuoi inviare i dati address
user_countrycodice paese ISO (es. IT)Necessario se vuoi inviare i dati address

Quindi, per un form lead generation standard, la configurazione più semplice e solida è quasi sempre: email + telefono in formato E.164. Se il form raccoglie anche dati di indirizzo, allora puoi estendere il mapping includendo nome, cognome, CAP e paese come blocco address. Google richiede anche la normalizzazione dei dati e il formato E.164 per il numero di telefono. :contentReference[oaicite:4]{index=4}


Debug e Verifica

Test in GTM Preview Mode

  1. Attiva la Preview Mode di GTM
  2. Vai sulla pagina con il form
  3. Compila tutti i campi e invia
  4. Nel debugger GTM, verifica che l’evento brevo_form_submit compaia nella timeline
  5. Clicca sull’evento e controlla che tutte le variabili siano popolate correttamente

Verifica in Console

Dopo aver inviato il form, esegui in console:

// Visualizza gli ultimi eventi nel dataLayer
dataLayer.filter(function(e) { return e.event; }).slice(-3);

Dovresti vedere l’evento brevo_form_submit con tutti i campi popolati.

Verifica Enhanced Conversions

  1. Google Tag Assistant: verifica che il tag di conversione riceva i parametri user_data
  2. Google Ads → Conversioni → Diagnostica: controlla che i dati vengano ricevuti e matchati

Tieni presente che Google segnala che la diagnostica e il matching delle Enhanced Conversions possono richiedere tempo per popolarsi dopo l’implementazione. :contentReference[oaicite:5]{index=5}


Troubleshooting

L’evento non compare nel dataLayer

  • Verifica che il form abbia l’ID corretto (sib-form o quello che hai configurato)
  • Verifica che XHR_FILTER e XHR_PARAM corrispondano alla chiamata reale (controlla in Network)
  • Verifica che lo script venga caricato prima del submit (usa DOM Ready come trigger)
  • Controlla nella tab Network che la chiamata restituisca status 2xx

Alcuni campi sono vuoti

  • I nomi dei campi (NOME, EMAIL, ecc.) sono case-sensitive: verifica che corrispondano esattamente agli attributi name nel form
  • Per il telefono Brevo, verifica che il widget prefisso abbia la classe .sib-sms-select
  • Esegui lo script di mappatura campi (Step 2 della Parte 1) per verificare i nomi corretti

Lo script viene eseguito due volte

  • Il flag window['__formTracker_' + FORM_ID] dovrebbe prevenirlo
  • Verifica che il tag GTM non abbia trigger multipli che lo fanno scattare più volte

Il form usa Fetch invece di XHR

Se il test della Parte 1 mostra 🟢 FETCH invece di 🔵 XHR, devi usare questo override alternativo:

<script>
(function() {
  var FORM_ID = 'il-tuo-form-id';
  var FETCH_FILTER = 'dominio-da-intercettare.com';
  var EVENT_NAME = 'form_submit';
  
  var form = document.getElementById(FORM_ID);
  if (!form) return;
  if (window['__formTracker_' + FORM_ID]) return;
  window['__formTracker_' + FORM_ID] = true;
  var origFetch = window.fetch;
  
  window.fetch = function(url, options) {
    var urlString = (typeof url === 'string') ? url : url.url;
    
    if (urlString && urlString.indexOf(FETCH_FILTER) !== -1) {
      // Cattura i dati prima dell'invio
      var formData = {
        user_email: form.querySelector('[name="email"]')?.value || ''
        // ... altri campi
      };
      
      // Esegui la fetch originale e traccia se ha successo
      return origFetch.apply(this, arguments).then(function(response) {
        if (response.ok) {
          window.dataLayer = window.dataLayer || [];
          window.dataLayer.push({
            event: EVENT_NAME,
            form_id: FORM_ID,
            user_email: formData.user_email
            // ... altri campi
          });
        }
        return response;
      });
    }
    
    return origFetch.apply(this, arguments);
  };
})();
</script>

Conclusioni

Il tracciamento dei form di terze parti richiede un approccio metodico:

  1. Analizza sempre il form con gli strumenti di debug prima di scrivere codice
  2. Identifica il tipo di invio (redirect, submit event, XHR, Fetch)
  3. Cattura i dati nel momento giusto — prima dell’invio, non dopo
  4. Verifica lo status code prima di tracciare (solo 2xx = successo)
  5. Previeni la doppia esecuzione con un flag univoco
  6. Standardizza le variabili per riutilizzare i tag su più form
  7. Formatta il telefono in E.164 per le Enhanced Conversions
  8. Mappa correttamente i dati utente in Google Ads: email da sola va bene, mentre nome e cognome hanno senso solo se accompagnati dall’indirizzo richiesto dalla piattaforma. :contentReference[oaicite:6]{index=6}

Seguendo questa metodologia potrai tracciare qualsiasi form — anche quelli più ostici — e raccogliere i dati necessari per migliorare la qualità della misurazione e delle ottimizzazioni in Google Ads. Le Enhanced Conversions for web usano infatti dati first-party forniti dall’utente, normalizzati e associati alla conversione per migliorare il matching e la misurazione. :contentReference[oaicite:7]{index=7}

Se tutto questo ti sembra troppo complicato — ed è normale che lo sia, perché tra form embedded, dataLayer, trigger GTM, Enhanced Conversions e debug tecnico gli errori sono dietro l’angolo — ci pensiamo noi. Se vuoi implementare un tracciamento affidabile senza perdere lead e senza sporcarti le mani con la parte tecnica, contattaci dal form.