Real User Monitoring WordPress: implementare RUM con JS

18 agosto 20267 minPerformance
In breveAI

Guida completa per implementare Real User Monitoring su WordPress: codice JavaScript per Core Web Vitals, endpoint REST API, strumenti e ottimizzazioni basate su dati reali.

Perché il Real User Monitoring è essenziale per WordPress

Il Real User Monitoring (RUM) misura le performance reali dei siti WordPress dal browser degli utenti, non da test sintetici. Mentre strumenti come Lighthouse o GTmetrix forniscono snapshot controllati, il RUM raccoglie dati da sessioni reali con variabili imprevedibili: connessioni lente, dispositivi obsoleti, estensioni browser invasive.

Per le agenzie che gestiscono decine di siti client, il RUM offre vantaggi concreti:

  • Rilevamento proattivo di regressioni di performance prima delle lamentele dei clienti
  • Dati segmentati per geografía, dispositivo e tipo di connessione
  • Correlazione diretta tra performance e metriche di business (conversioni, bounce rate)
  • Validazione degli interventi di ottimizzazione con dati reali

Dal 2021, Google considera i Core Web Vitals fattore di ranking. Monitorarli con RUM è diventato un requisito operativo, non un optional.

Core Web Vitals e Performance API

I tre pilastri dei Core Web Vitals richiedono misurazioni specifiche tramite JavaScript:

Largest Contentful Paint (LCP)

Misura quando l’elemento principale del viewport diventa visibile. L’API Performance Observer lo intercetta così:

const observer = new PerformanceObserver((list) => {
  const entries = list.getEntries();
  const lastEntry = entries[entries.length - 1];
  console.log('LCP:', lastEntry.renderTime || lastEntry.loadTime);
});
observer.observe({ type: 'largest-contentful-paint', buffered: true });

Target: sotto 2.5 secondi. Sul campo, siti WordPress complessi raggiungono 3-4 secondi senza ottimizzazioni.

First Input Delay (FID) e Interaction to Next Paint (INP)

FID misura la latenza del primo input utente. Dal marzo 2024, Google lo ha sostituito con INP che valuta tutte le interazioni. Implementazione INP:

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    const duration = entry.processingStart - entry.startTime;
    console.log('INP candidate:', duration, entry.name);
  }
});
observer.observe({ type: 'event', buffered: true, durationThreshold: 16 });

Target INP: sotto 200ms. Plugin JavaScript pesanti (slider, form builder) sono i principali colpevoli di valori alti.

Cumulative Layout Shift (CLS)

Misura gli spostamenti inattesi di layout. Critico per WordPress dove immagini senza dimensioni e banner dinamici causano problemi:

let clsValue = 0;
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    if (!entry.hadRecentInput) {
      clsValue += entry.value;
    }
  }
});
observer.observe({ type: 'layout-shift', buffered: true });

Target: sotto 0.1. Temi con lazy loading mal implementato superano facilmente 0.3.

Implementare un sistema RUM completo su WordPress

Un’implementazione production-ready richiede raccolta dati, batching, invio beacon e gestione errori. Ecco uno script completo da inserire nel footer:

<script>
(function() {
  const endpoint = '/wp-json/agencypilot/v1/rum';
  const metrics = {};
  const sessionId = Date.now() + Math.random().toString(36);

  function sendMetrics(data) {
    const payload = {
      url: window.location.href,
      sessionId: sessionId,
      userAgent: navigator.userAgent,
      connection: navigator.connection?.effectiveType,
      timestamp: Date.now(),
      ...data
    };

    if (navigator.sendBeacon) {
      navigator.sendBeacon(endpoint, JSON.stringify(payload));
    } else {
      fetch(endpoint, {
        method: 'POST',
        body: JSON.stringify(payload),
        keepalive: true
      });
    }
  }

  // LCP
  new PerformanceObserver((list) => {
    const entries = list.getEntries();
    const lastEntry = entries[entries.length - 1];
    metrics.lcp = lastEntry.renderTime || lastEntry.loadTime;
  }).observe({ type: 'largest-contentful-paint', buffered: true });

  // FID (legacy)
  new PerformanceObserver((list) => {
    const entry = list.getEntries()[0];
    metrics.fid = entry.processingStart - entry.startTime;
  }).observe({ type: 'first-input', buffered: true });

  // CLS
  let clsValue = 0;
  new PerformanceObserver((list) => {
    for (const entry of list.getEntries()) {
      if (!entry.hadRecentInput) {
        clsValue += entry.value;
      }
    }
    metrics.cls = clsValue;
  }).observe({ type: 'layout-shift', buffered: true });

  // Navigation Timing
  window.addEventListener('load', () => {
    const nav = performance.getEntriesByType('navigation')[0];
    metrics.ttfb = nav.responseStart - nav.requestStart;
    metrics.domLoad = nav.domContentLoadedEventEnd - nav.fetchStart;
    metrics.windowLoad = nav.loadEventEnd - nav.fetchStart;
  });

  // Invio al page hide
  window.addEventListener('visibilitychange', () => {
    if (document.visibilityState === 'hidden') {
      sendMetrics(metrics);
    }
  });

  // Fallback dopo 10 secondi
  setTimeout(() => sendMetrics(metrics), 10000);
})();
</script>

Questo script raccoglie metriche essenziali e le invia tramite sendBeacon (garantito anche se l’utente chiude la tab) o fetch con keepalive.

Backend WordPress: REST API endpoint

Lato server serve un endpoint per ricevere i dati. Esempio minimale in functions.php:

add_action('rest_api_init', function() {
  register_rest_route('agencypilot/v1', '/rum', [
    'methods' => 'POST',
    'callback' => 'handle_rum_data',
    'permission_callback' => '__return_true'
  ]);
});

function handle_rum_data($request) {
  $data = json_decode($request->get_body(), true);
  
  // Validazione base
  if (empty($data['lcp']) || empty($data['sessionId'])) {
    return new WP_Error('invalid_data', 'Missing required fields', ['status' => 400]);
  }

  // Salvataggio in custom table o invio a servizio esterno
  global $wpdb;
  $wpdb->insert(
    $wpdb->prefix . 'rum_metrics',
    [
      'session_id' => sanitize_text_field($data['sessionId']),
      'url' => esc_url_raw($data['url']),
      'lcp' => floatval($data['lcp']),
      'fid' => floatval($data['fid'] ?? 0),
      'cls' => floatval($data['cls']),
      'ttfb' => floatval($data['ttfb']),
      'user_agent' => sanitize_text_field($data['userAgent']),
      'connection' => sanitize_text_field($data['connection'] ?? 'unknown'),
      'created_at' => current_time('mysql')
    ]
  );

  return ['success' => true];
}

Per volumi alti (>100k pageview/mese), conviene inviare i dati a servizi esterni come Elasticsearch o ClickHouse invece di salvare in MySQL.

Strumenti e servizi RUM pronti all’uso

Implementare RUM da zero è istruttivo ma time-consuming. Alternative commerciali mature:

Google Analytics 4 + Web Vitals

Dal 2022 GA4 raccoglie automaticamente Core Web Vitals. Integrazione con la libreria ufficiale:

<script type="module">
import {onCLS, onFID, onLCP} from 'https://unpkg.com/web-vitals@3?module';

function sendToGA({name, delta, id}) {
  gtag('event', name, {
    event_category: 'Web Vitals',
    value: Math.round(delta),
    event_label: id,
    non_interaction: true
  });
}

onCLS(sendToGA);
onFID(sendToGA);
onLCP(sendToGA);
</script>

Pro: gratuito fino a 10M eventi/mese, integrato con Analytics. Contro: sampling aggressivo, limitazioni query.

Soluzioni dedicate

  • SpeedCurve: il più completo, $20/mese per sito. Heatmap CLS, filmstrip, correlazione con business metrics
  • Datadog RUM: enterprise grade, ottimo per agenzie con infrastruttura complessa. Da $1.27 per 10k sessioni
  • Sentry Performance: focus su errori JS + performance. $26/mese per 50k transazioni
  • Cloudflare Web Analytics: gratuito, privacy-first, limiti nelle funzionalità avanzate

Per agenzie con portfolio 20+ siti, strumenti centralizzati come AgencyPilot che aggregano RUM da tutti i client fanno risparmiare ore di dashboard hopping.

Ottimizzare in base ai dati RUM

Raccogliere dati è inutile senza azione. Pattern comuni rilevati via RUM su WordPress:

LCP alto su mobile

Cause frequenti:

  • Hero image non ottimizzata: serve responsive images con srcset e formati moderni (WebP, AVIF)
  • Font web bloccanti: usare font-display: swap e preload selettivo
  • CSS render-blocking: critical CSS inline, defer del resto

CLS da advertising e lazy loading

Soluzioni:

  • Riservare spazio con aspect-ratio CSS per banner dinamici
  • Specificare width e height su tutte le immagini
  • Evitare lazy loading above-the-fold

INP degradato da plugin

Con RUM si identificano eventi specifici lenti. Colpevoli tipici: slider Swiper con troppe slide, form validation sincrona, mega menu con animazioni complesse. Soluzione: code splitting, web workers per operazioni pesanti, debouncing eventi.

Privacy e GDPR compliance

Il RUM raccoglie dati utente. Considerazioni legali:

  • IP anonymization: troncare gli ultimi ottetti prima di salvare
  • No PII: escludere parametri URL sensibili (email, token)
  • Cookie banner: alcuni interpretano RUM come tracking, altri no. Posizione conservativa: richiedere consenso analytics
  • Data retention: cancellare metriche dopo 90-180 giorni

Cloudflare Web Analytics e Plausible sono alternative privacy-first che non richiedono consenso in UE.

FAQ

Qual è la differenza tra RUM e Synthetic Monitoring?

Il Synthetic Monitoring (Lighthouse, WebPageTest) esegue test controllati da location e configurazioni predefinite. Il RUM raccoglie dati da utenti reali con infinite variabili: dispositivi, connessioni, estensioni browser. Synthetic è ottimo per baseline e regression testing, RUM per capire l’esperienza reale degli utenti. Servono entrambi.

Quanto impatta il codice RUM sulle performance?

Gli script RUM ben scritti pesano 2-5KB gzipped e usano API native del browser (PerformanceObserver) con overhead minimo. L’impatto misurato è sotto 50ms sul load time totale. Script mal implementati con polling continuo o invii sincroni possono degradare proprio le metriche che cercano di misurare. Usare sempre sendBeacon o fetch con keepalive.

Quanti dati servono per avere statistiche significative?

Dipende dal traffico. Per siti con 1000+ pageview/giorno, una settimana fornisce dati sufficienti per identificare pattern. Sotto 500 pageview/giorno servono 2-4 settimane. Segmentare sempre per device type e connection: mobile 3G ha performance radicalmente diverse da desktop su fibra. Il 75° percentile (P75) è la metrica standard per valutare Core Web Vitals.

Come integrare RUM in workflow CI/CD?

Impostare alert automatici quando le metriche RUM superano soglie per più di X% delle sessioni. Esempio: se P75 LCP supera 3 secondi per 48 ore consecutive, bloccare i deploy e investigare. Strumenti come SpeedCurve offrono API per interrogare metriche RUM da pipeline CI. Alternativa: webhook che notificano Slack quando regressioni sono rilevate.

RUM funziona con cache e CDN?

Sì, anzi è ancora più importante. Con cache aggressive (Cloudflare, Varnish) i test sintetici vedono performance irrealisticamente alte. Il RUM misura l’esperienza reale includendo cold cache, miss CDN, variabilità geografica. Per siti WordPress con cache multi-livello, solo RUM rivela problemi come cache warming lento o edge location sottoperformanti.

Gestisci i siti WordPress dei tuoi clienti?

AgencyPilot ti dà report AI, uptime monitoring, backup e portale clienti in un’unica dashboard. Gratis per 3 siti.

Prova gratis
Leggi anche
Tutti gli articoli
Tutti gli articoli