Indice dei contenuti dell'articolo:
Prendete un sito WordPress ospitato su LiteSpeed Web Server, installate LiteSpeed Cache (LSCache) e poi eseguite due test apparentemente equivalenti sulla versione Mobile di Lighthouse, quella normalmente più severa.
Il profilo mobile simula infatti un ambiente molto meno favorevole rispetto alla classica fibra dell’ufficio: connessione 4G lenta, latenza più alta e un Motorola Moto G Power (2022) emulato, smartphone di fascia medio-bassa pensato proprio per rappresentare hardware non particolarmente potente.
La scelta ha senso perché le performance dipendono anche dalla capacità del dispositivo di elaborare HTML, CSS e soprattutto JavaScript. Un sito che sembra istantaneo su desktop può diventare molto più lento su uno smartphone economico.
È quindi un banco prova volutamente difficile.
Il primo con Google PageSpeed Insights.
Risultato:
- Performance: 100;
- First Contentful Paint: 1,1 secondi;
- Largest Contentful Paint: 1,1 secondi;
- Total Blocking Time: 0 millisecondi;
- Speed Index: 1,1 secondi;
- CLS: 0.
Un sito apparentemente perfetto.

Poi aprite la stessa pagina con Chrome, lanciate Lighthouse direttamente dal browser e ottenete:
- Performance: 77;
- First Contentful Paint: 3 secondi;
- Largest Contentful Paint: 4,5 secondi;
- Total Blocking Time: 170 millisecondi;
- Speed Index: 3 secondi;
- CLS: 0.

Naturalmente due esecuzioni Lighthouse possono differire leggermente. Hardware, CPU, versione di Chrome, throttling e condizioni della macchina possono modificare il risultato.
Ma quando passiamo da 77 a 100, da un LCP di 4,5 secondi a 1,1 secondi e soprattutto da 170 millisecondi di Total Blocking Time a zero, vale la pena smettere di guardare il cerchio verde e iniziare a guardare il codice.
Lo abbiamo fatto.
Abbiamo clonato il repository ufficiale litespeedtech/lscache_wp su GitHub e letto il sorgente della versione corrente al momento in cui scriviamo, la 7.9.1. Tutti i file, i metodi e le righe citate in questo articolo sono riferite a quella versione e chiunque può verificarle.
E nel codice di LiteSpeed Cache il meccanismo è molto più chiaro di quanto ci aspettassimo.
LiteSpeed Cache è in grado di riconoscere determinate richieste di test e di lasciarle su una variante della pagina nella quale il JavaScript viene ritardato aggressivamente, mentre un browser reale viene fatto transitare, dopo la prima visita, sulla versione normale.
Se vogliamo utilizzare una parola semplice, questa per noi è una forma di cloaking del benchmark.
Non stiamo parlando del normale Delay JavaScript
Facciamo subito una distinzione importante.
Ritardare il JavaScript non è di per sé qualcosa di scorretto.
Lo fanno WP Rocket, FlyingPress, Perfmatters e molti altri strumenti di performance.
Defer, delay, minificazione, Critical CSS, lazy loading e riduzione del CSS inutilizzato sono tecniche perfettamente valide e, quando applicate correttamente agli utenti reali, possono produrre miglioramenti concreti.
Il problema qui è differente.
Il problema nasce quando il software è in grado di capire chi lo sta misurando e mantenere il tester in una condizione differente da quella nella quale finirà normalmente il browser dell’utente.
È la differenza tra:
“ottimizzo il mio sito”
e:
“quando arriva il banco prova, attivo la configurazione che fa ottenere il voto migliore”.
E il codice di LiteSpeed Cache ci permette di seguire questo percorso passo dopo passo.
Primo passaggio: LiteSpeed mantiene una lista dei tester
Nel repository ufficiale di LiteSpeed Cache esiste un file chiamato data/gm_uas.txt.
È la lista di User-Agent utilizzata dal sistema Guest Mode.
Nel file troviamo esplicitamente:
Lighthouse GTmetrix Google Pingdom bot spider PTST HeadlessChrome
Non stiamo inferendo il comportamento da un nome di variabile oscuro.
Lighthouse è scritto letteralmente nella lista.
Così come Google, GTmetrix, Pingdom e HeadlessChrome. La corrispondenza è una regular expression case-insensitive costruita concatenando le voci con |: basta che una di queste stringhe compaia da qualche parte nello User-Agent. La voce Google, ad esempio, intercetta sia PageSpeed Insights sia Googlebot.
Ma accanto a quel file ce n’è un secondo, che nella discussione pubblica viene quasi sempre ignorato: data/gm_ips.txt.
È la lista degli indirizzi IP da trattare sempre come Guest. Le prime righe sono queste:
66.102.0.0/20 66.249.64.0/19 74.125.0.0/16 142.250.0.0/15 172.255.48.128/27 172.255.61.32/28 208.70.247.157
seguite da una quarantina di IP singoli appartenenti a datacenter cloud.
I primi quattro blocchi sono range di Google: 66.249.64.0/19 è il range storico di Googlebot, gli altri sono infrastruttura Google generica, cioè anche quella da cui partono i test di PageSpeed Insights. I due blocchi 172.255.x sono GTmetrix.
Questo significa una cosa precisa: anche se un tester cambiasse User-Agent, verrebbe comunque riconosciuto dall’indirizzo di provenienza. Il riconoscimento è doppio.
Dalla 7.7 la lista dei tester si aggiorna da sola, e l’amministratore non può più modificarla
Fino alla versione 7.6 le due liste erano configurabili dal pannello, nella scheda General → Tuning, con le opzioni “Guest Mode User Agents” e “Guest Mode IPs”.
Con la versione 7.7 è cambiato tutto, e il changelog ufficiale lo dice in una riga:
* **Cloud** Guest Mode IP/UA lists now sync automatically from the QUIC.cloud API.
Nel sorgente il meccanismo è in src/guest.cls.php: un cron giornaliero (litespeed_task_guest_sync) scarica da https://wpapi.quic.cloud/gm_ips e https://wpapi.quic.cloud/gm_uas le liste aggiornate e le salva in wp-content/litespeed/cloud/gm_ips.txt e gm_uas.txt.
Il metodo che carica le liste, _load_gm_list(), ha una priorità esplicita:
// Try cloud synced file first, then fallback to plugin data file
$files = [
LSCWP_CONTENT_FOLDER . '/litespeed/cloud/' . $filename,
dirname( __DIR__ ) . '/data/' . $filename,
];
Il file sincronizzato dal cloud vince sul file del plugin.
E la routine di aggiornamento in src/data.upgrade.func.php fa il resto:
Debug2::debug( '[Data] v7.7 upgrade: dropping guest_ips/guest_uas options' ); Conf::delete_option( 'conf.guest_ips' ); Conf::delete_option( 'conf.guest_uas' );
Le due opzioni vengono cancellate dal database al momento dell’aggiornamento e nel codice sono marcate come deprecated, da rimuovere dopo la versione 9.
Il risultato pratico è che oggi l’elenco di chi viene tenuto in Guest Mode non lo decide più chi amministra il sito: lo decide QUIC.cloud, l’azienda che sviluppa il plugin, e lo può aggiornare in qualsiasi momento su tutte le installazioni senza rilasciare una nuova versione. Se domani nasce un nuovo servizio di test, basta aggiungere una riga lato server.
Secondo passaggio: il codice confronta User-Agent e IP
Nel file lib/guest.cls.php troviamo il metodo always_guest().
Il file è volutamente leggero e viene caricato da guest.vary.php senza avviare WordPress: lo dice il commento in testa al file stesso.
Il funzionamento è semplice.
Carica la lista degli User-Agent, costruisce una regular expression e la confronta con:
$_SERVER['HTTP_USER_AGENT']
Se trova una corrispondenza restituisce true.
Subito dopo viene effettuato anche il controllo sull’indirizzo IP, che dalla 7.7 supporta anche la notazione CIDR (ed è per questo che nella lista compaiono interi range Google).
In termini semplificati il codice fa questo:
if (user_agent_presente_nella_lista()) {
return true;
}
if (ip_presente_nella_lista()) {
return true;
}
return false;
Questa è una rappresentazione semplificata, ma la logica del sorgente originale è esattamente questa.
Quindi LiteSpeed dispone esplicitamente di una funzione che risponde alla domanda:
“Questa richiesta appartiene a uno dei client che voglio mantenere sempre in Guest Mode?”
Terzo passaggio: chi entra in Guest Mode, e chi non ne esce
Ed è qui che la questione diventa molto più interessante, perché il meccanismo ha due cancelli distinti.
Il primo cancello è nel plugin, dentro WordPress, nel metodo _maybe_guest_mode() di src/vary.cls.php. E qui la sorpresa: questo metodo non guarda affatto User-Agent o IP. Guarda solo una cosa:
// If vary is set, then not a guest.
if ( self::has_vary() ) {
return;
}
Se la richiesta non porta il cookie _lscache_vary, è Guest. Punto. Chiunque arrivi senza quel cookie, uomo o macchina, riceve la variante Guest, e se Guest Optimization è attiva viene definita la costante LITESPEED_GUEST_OPTM che vedremo tra poco.
Il secondo cancello è in guest.vary.php, ed è quello che decide chi riceve il cookie e quindi chi esce dalla Guest Mode.
LiteSpeed inserisce infatti in fondo alla pagina Guest un piccolo script (assets/js/guest.js) che, se il cookie non c’è, fa una richiesta POST verso guest.vary.php. Nel metodo update_guest_vary() troviamo il passaggio decisivo:
if ( $this->always_guest() ) {
echo '[]';
exit;
}
Tradotto in italiano:
se sei uno dei client che LiteSpeed riconosce come “always guest”, non ti do il cookie e non ti dico di ricaricare la pagina normale.
Il processo termina.
Per gli altri visitatori, invece, LiteSpeed imposta il vary cookie (valido due giorni) e restituisce una risposta che ordina il reload:
setcookie( self::$_vary_name, $vary, $expire, '/', false, $is_ssl, true ); echo json_encode( [ 'reload' => 'yes' ] );
E lo script lato browser esegue letteralmente:
if (data.hasOwnProperty('reload') && data.reload == 'yes') {
sessionStorage.setItem('litespeed_docref', document.referrer);
sessionStorage.setItem('litespeed_reloaded', '1');
window.location.reload(true);
}
Il browser reale ricarica quindi la pagina e prosegue con la variante corretta.
Il tester riconosciuto attraverso User-Agent o IP, invece, resta sulla variante Guest altamente ottimizzata.
Questa differenza è fondamentale.
Il prezzo nascosto per l’utente reale: la pagina si carica due volte
Fermiamoci un attimo su quel window.location.reload(true), perché la narrazione ufficiale della Guest Mode è “rendere velocissima la prima visita”.
Quello che succede davvero al primo visitatore reale è questo:
- riceve la pagina Guest dalla cache;
- la renderizza;
- lo script contatta
guest.vary.php, che avvia PHP (senza WordPress) e imposta il cookie; - il browser ricarica l’intera pagina;
- riceve la variante normale, quella con il JavaScript configurato dall’amministratore.
La “prima visita velocissima” è quindi una pagina che l’utente vede per una frazione di secondo prima che venga sostituita da un secondo caricamento completo. Il costo reale della prima visita è la somma dei due.
Che il reload sia un problema noto lo dimostra un dettaglio nel codice: LiteSpeed inserisce nell’<head> un secondo script, guest.docref.js, che dopo il reload sovrascrive document.referrer con il valore salvato prima del reload:
Object.defineProperty(document, 'referrer', {
get: function () {
return litespeed_docref;
},
});
Serve perché altrimenti Google Analytics e simili vedrebbero il sito stesso come referrer di tutte le visite e il traffico organico sparirebbe dai report. Hanno dovuto scrivere una patch per riparare un effetto collaterale del proprio meccanismo. Il tester, che non ricarica mai, di tutto questo non vede nulla.
Ed è qui che PageSpeed Insights e Chrome prendono due strade differenti
Immaginiamo PageSpeed Insights.
La richiesta arriva dall’infrastruttura di Google, quindi da un IP nei range di gm_ips.txt, con uno User-Agent che contiene Chrome-Lighthouse. Non ha cookie, perché ogni test parte da un profilo pulito.
Primo cancello: niente cookie, quindi variante Guest.
Secondo cancello: lo script chiama guest.vary.php, always_guest() risponde true, nessun cookie, nessun reload.
Il test viene condotto interamente sulla variante Guest.
Ora prendiamo Chrome sul nostro computer.
È il browser con cui abbiamo già aperto il sito almeno una volta. Ha quindi già il cookie _lscache_vary, oppure lo riceve al primo caricamento e ricarica. Da quel momento, per il primo cancello, Chrome non è più un Guest, indipendentemente da cosa dichiari lo User-Agent: il cookie è la sola cosa che il plugin controlla lato WordPress.
Lighthouse lanciato da DevTools misura quindi la variante normale, quella che vede chiunque abbia già visitato il sito. Cioè quasi tutti i vostri utenti.
Abbiamo così due percorsi:
e il test fatto direttamente da Lighthouse su Chrome:
A questo punto manca soltanto l’ultimo pezzo ed una domanda che sorge spontanea: che cosa fa Guest Optimization al JavaScript?
Quarto passaggio: Guest Optimization forza il JavaScript in modalità Delay
Apriamo src/optimize.cls.php.
Il plugin prima legge la normale configurazione JavaScript impostata dall’amministratore.
Poi incontra questo controllo:
$this->cfg_js_defer = $this->conf(self::O_OPTM_JS_DEFER);
if (defined('LITESPEED_GUEST_OPTM')) {
$this->cfg_js_defer = 2;
}
Questa riga è particolarmente importante.
Quando Guest Optimization è attiva, LiteSpeed forza internamente il valore JavaScript a 2.
Nel codice del plugin il valore 2 rappresenta la modalità delayed.
Non è quindi soltanto un normale defer.
È la modalità nella quale gli script vengono neutralizzati e caricati successivamente.
E questo override avviene anche se la configurazione ordinaria del sito prevede un comportamento differente.
Non è un’ipotesi accademica: è esattamente ciò che fanno i preset ufficiali del plugin. Nei file data/preset/advanced.data e data/preset/aggressive.data, i due profili “consigliati” per la maggior parte dei siti, troviamo:
["guest",true] ["guest_optm",true] ["optm-js_defer",1]
Guest Mode attiva, Guest Optimization attiva, e JavaScript a 1, cioè semplice defer, per tutti gli altri.
Il preset stesso configura la divergenza: il tester ottiene il delay (2), l’utente reale ottiene il defer (1). Un amministratore che applica il preset “Advanced” con un click ha, senza saperlo, due siti diversi.
Solo il preset “Extreme” imposta 2 per tutti, e lì, almeno, il tester e l’utente vedono la stessa cosa.
Non solo JavaScript: cosa forza davvero Guest Optimization
Cercando LITESPEED_GUEST_OPTM nel sorgente si scopre che il JavaScript è solo la voce più vistosa. Quando quella costante è definita, il plugin forza indipendentemente dalle impostazioni dell’amministratore:
- minificazione e combinazione di CSS e JS (
optimize.cls.php); - caricamento asincrono del CSS e UCSS, se il sito è collegato a QUIC.cloud;
- rimozione dei Google Fonts (
cfg_ggfonts_rmforzato atrue): il tester vede una pagina senza web font esterni; - minificazione HTML e controllo del DNS prefetch;
- lazy load di immagini e iframe, Viewport Images, sostituzione WebP, aggiunta delle dimensioni mancanti alle immagini (
media.cls.php); - rimozione dei tag
<noscript>(cfg_trim_noscript), cioè dei fallback che normalmente accompagnano il lazy load; - placeholder responsive per le immagini (
placeholder.cls.php).
E soprattutto salta i controlli di sicurezza che l’amministratore avrebbe a disposizione. In optimize.cls.php:
if (!defined('LITESPEED_GUEST_OPTM')) {
if (!Control::is_cacheable()) {
return $content;
}
// Check if hit URI excludes
...
}
Le “URI Excludes” dell’ottimizzazione vengono applicate solo quando non si è in Guest Optimization. Lo stesso vale per i “Role Excludes” in core.cls.php. E per il JavaScript viene ignorata la normale lista “JS Deferred Excludes” a favore di una lista separata, “Guest Mode JS Excludes”, che di default è vuota.
In altre parole: tutto ciò che l’amministratore ha escluso perché sa che si rompe, per il tester viene ottimizzato lo stesso. Il tester non naviga, non compila form, non apre menu: non si accorgerà mai che qualcosa è rotto.
Quinto passaggio: il tag script smette di essere uno script
Vediamo ora cosa accade concretamente al markup.
Un normale file JavaScript sarebbe dichiarato così:
<script src="/wp-content/themes/theme/app.js"></script>
Un browser che incontra questo elemento riconosce src e può richiedere il file JavaScript.
Quando LiteSpeed utilizza la modalità delayed, il tag viene trasformato in questo (la riga è in optimize.cls.php):
<script data-optimized="1" type="litespeed/javascript" data-src="/wp-content/themes/theme/app.js"></script>
Ed ecco il punto fondamentale.
type="litespeed/javascript" non è un normale tipo JavaScript riconosciuto dal browser.
E soprattutto l’URL non è più nell’attributo src.
È stato spostato in:
data-src="..."
Per Chrome quel file non è quindi, in quel momento, una risorsa JavaScript da scaricare ed eseguire.
Il riferimento esiste nell’HTML, ma la richiesta di rete verso quel file non parte come partirebbe con un normale tag script.
Lo stesso trattamento viene riservato agli script inline, il cui contenuto resta nel tag con type="litespeed/javascript", e agli iframe: src diventa data-litespeed-src, quindi embed YouTube, mappe e advertising non vengono caricati finché non c’è interazione.
C’è anche un dettaglio meno noto. In optimize.cls.php, quando il delay è attivo, il plugin modifica il contenuto stesso dei file JavaScript che serve:
$con = str_replace('DOMContentLoaded', 'DOMContentLiteSpeedLoaded', $con);
Ogni script che aspetta DOMContentLoaded viene riscritto per aspettare un evento inventato da LiteSpeed, che il loader emetterà solo dopo aver caricato tutti gli script ritardati. Non è più il vostro JavaScript: è il vostro JavaScript con le stringhe sostituite.
Questa distinzione è importante anche terminologicamente.
Non diciamo che LiteSpeed cancella necessariamente ogni riferimento al JavaScript.
Diciamo una cosa tecnicamente più precisa e ancora più significativa: il file JavaScript viene sottratto al normale caricamento del browser, o meglio alla sua esecuzione.
Sesto passaggio: quando partono davvero quei JavaScript?
Serve naturalmente un loader che successivamente trasformi quegli elementi nuovamente in normali script.
Il file di LiteSpeed si chiama js_delay.min.js, e la versione non minificata assets/js/js_delay.js si legge in un minuto.
E qui troviamo l’ultimo tassello.
Il loader registra eventi dell’utente:
window.litespeed_ui_events = window.litespeed_ui_events || ['mouseover', 'click', 'keydown', 'wheel', 'touchmove', 'touchstart', 'pointerup', 'pointerdown'];
Quando avviene una di queste interazioni, parte la funzione di caricamento ritardato.
Il loader cerca gli elementi:
script[type="litespeed/javascript"]
e soltanto in quel momento crea normali elementi <script>, trasformando data-src nuovamente in src, caricandoli uno alla volta, in sequenza.
A quel punto il browser scarica il JavaScript.
Prima dell’interazione, no.
E poi c’è questa riga, che nel sorgente è commentata:
// const litespeed_js_delay_timer = setTimeout( litespeed_load_delayed_js, 70 );
Un fallback temporale esisteva, o era stato quantomeno previsto: dopo 70 millisecondi gli script sarebbero partiti comunque, interazione o no. È stato disattivato. Senza interazione, il JavaScript ritardato non parte mai. Né dopo 70 millisecondi, né dopo 70 secondi. Un utente che apre la pagina e legge senza toccare nulla ha una pagina il cui JavaScript non è mai stato eseguito.
Con un timer, un test Lighthouse che dura diversi secondi avrebbe visto partire gli script e ne avrebbe misurato il costo. Senza timer, non li vede.
E Lighthouse non è un utente che muove il mouse
Ora rimettiamo insieme tutti i pezzi.
PageSpeed Insights avvia Lighthouse.
LiteSpeed riconosce la richiesta attraverso le proprie liste UA/IP, che oggi vengono aggiornate direttamente da QUIC.cloud.
La mantiene nella variante Guest, non consegnandole mai il cookie.
Guest Optimization forza il JavaScript in modalità Delay, anche se il preset del sito dice defer.
I normali src diventano data-src.
I tag diventano type="litespeed/javascript".
Il caricamento reale avviene quando si verifica un’interazione utente, e solo allora.
Ma un test Lighthouse non utilizza la pagina come una persona che muove il mouse, gira la rotella, tocca lo schermo o apre il menu.
Il risultato pratico è evidente.
Durante la finestra nella quale vengono misurate le performance, il codice JavaScript non viene eseguito.
Ed ecco come si arriva a:
Total Blocking Time: 0 ms.
Non perché il JavaScript del sito sia diventato magicamente gratuito, ma perché quel lavoro non sta avvenendo durante il test.
Come verificarlo sul vostro sito in due minuti
Non serve fidarsi di noi. Bastano due richieste curl dalla shell di un server qualunque.
La prima simula il tester: nessun cookie, User-Agent che contiene Lighthouse.
curl -s -A "Mozilla/5.0 (Linux; Android 11; moto g power (2022)) Chrome-Lighthouse" https://www.esempio.it/ \ | grep -c 'type="litespeed/javascript"'
La seconda simula un utente di ritorno: stesso URL, ma con il cookie di vary presente (il valore non è importante, il plugin controlla solo che esista).
curl -s -A "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/128.0.0.0" \ -b "_lscache_vary=1" https://www.esempio.it/ \ | grep -c 'type="litespeed/javascript"'
Se il primo comando restituisce decine di occorrenze e il secondo zero, avete appena osservato le due varianti con i vostri occhi. Per confronto, il plugin stesso espone un parametro documentato per forzare la variante non-Guest: ?litespeed_guest_off=1.
Se invece volete un numero PageSpeed onesto senza toccare il sito, l’unico modo è disattivare Guest Optimization, oppure impostare per tutti lo stesso livello che ricevono i tester. In entrambi i casi il punteggio scenderà. Quello è il numero vero.
Il test vede una pagina che l’utente non utilizzerà nello stesso modo
Questa, per noi, è la definizione pratica del problema.
PageSpeed Insights dovrebbe rispondere alla domanda:
“Come si comporta questa pagina?”
Ma se la pagina è in grado di riconoscere PageSpeed Insights e cambiare il proprio comportamento, il test finisce per rispondere a una domanda differente:
“Come si comporta questa pagina quando sa di essere sotto PageSpeed Insights?”
Sono due cose completamente diverse.
E c’è un corollario che riguarda la SEO in senso stretto. La voce Google nella lista UA e il range 66.249.64.0/19 nella lista IP intercettano Googlebot. Anche il crawler che indicizza il sito, quindi, riceve la variante Guest, con il JavaScript neutralizzato e i <noscript> rimossi. Che cosa veda esattamente il rendering di Googlebot su un sito il cui JavaScript parte solo al mouseover è una domanda che ogni proprietario di un sito con contenuti generati lato client dovrebbe porsi.
Il parallelismo Volkswagen diventa quasi imbarazzante
Torniamo per un momento al Dieselgate. Ve lo ricordate?
La centralina Volkswagen non rendeva l’automobile inesistente durante il test.
Il motore era sempre lì.
Le componenti erano sempre quelle.
Quello che cambiava era il comportamento quando la vettura riconosceva le condizioni del banco prova.
Durante l’omologazione attivava una modalità particolarmente favorevole, motore pulito, basse emissioni, un motore apparentemente non inquinante e rispettoso dell’ambiente.
Fuori dal banco prova tornava a comportarsi diversamente e scoppiò lo scandalo dei test truccati.
Con LiteSpeed Cache il parallelismo tecnologico non è ovviamente letterale, ma quello logico è difficile da ignorare.
Qui abbiamo:
- un software che deve essere misurato;
- un sistema che identifica caratteristiche del misuratore, per User-Agent e per indirizzo IP;
- una lista dei misuratori aggiornata centralmente dal produttore;
- una modalità speciale associata alle richieste qualificate;
- un comportamento differente nel caricamento delle risorse;
- un risultato del benchmark enormemente migliore.
Il tester arriva e il motore cambia mappatura.
Solo che qui la mappatura è fatta di PHP, cookie, User-Agent, range IP, data-src e JavaScript ritardato.
La stessa LiteSpeed ammette che il risultato può mascherare i problemi
A rendere questa vicenda ancora più sorprendente è la documentazione ufficiale.
LiteSpeed spiega esplicitamente che Guest Optimization applica il massimo livello di ottimizzazione alla pagina Guest anche quando alcune funzionalità sarebbero normalmente disattivate.
Dice inoltre che le richieste qualificanti comprendono bot, determinati User-Agent e prime visite.
E cita espressamente PageSpeed Insights e GTmetrix spiegando che Guest Optimization può migliorare molto i page speed score perché questi servizi ricevono una pagina cached e altamente ottimizzata.
Poi arriva la parte più significativa.
La documentazione avverte che Guest Optimization può mascherare i problemi reali e suggerisce di disabilitarla se si desidera ottenere una valutazione accurata dai siti di performance testing.
È una frase straordinaria.
Perché equivale sostanzialmente a dire: se volete sapere davvero quali problemi ha il sito, spegnete la funzione che gli fa ottenere il voto migliore.
Le comunicazioni sono ovviamente con toni molto pacati e politically correct, non scrivono mica “Stiamo barando i test PageSpeed per vendere di più”, ma di fatto è quello che fanno.
Va detto, per completezza, che nell’installazione da zero Guest Mode e Guest Optimization sono disattivate. Vengono attivate dai preset “Advanced”, “Aggressive” ed “Extreme”, cioè esattamente i profili che vengono consigliati a chi vuole “più velocità” con un click, e da buona parte delle guide di configurazione pubblicate dai provider che vendono hosting LiteSpeed.
Il danno vero è psicologico: il 100 interrompe l’ottimizzazione e la cazzimma !
Ed è questo il motivo per cui riteniamo questa scelta particolarmente dannosa.
Il problema non è soltanto tecnico.
È cognitivo.
Un sito che ottiene 77 genera domande.
Perché il Largest Contentful Paint impiega 4,5 secondi?
Perché abbiamo 170 millisecondi di Total Blocking Time?
Quanto JavaScript stiamo caricando?
Quale plugin sta bloccando il main thread?
Possiamo eliminare qualche libreria?
Il tema è troppo pesante?
Gli advertising script partono troppo presto?
Le immagini sono correttamente dimensionate?
Il CSS è eccessivo?
Un 77 spinge uno sviluppatore serio ad aprire DevTools e lavorare.
Un 100 dice invece che il lavoro è finito.
Ed è qui che un benchmark eccessivamente favorevole produce il danno maggiore.
Il cliente vede 100.
L’agenzia vede 100.
Il responsabile marketing vede 100.
Chi dovrebbe autorizzare ulteriori ore di ottimizzazione si domanda perché spendere soldi su un sito che Google apparentemente valuta perfetto.
Il problema reale rimane.
Ma nessuno lo cerca più.
E c’è un dato che nessun preset può truccare: i Core Web Vitals che Google usa per il ranking non arrivano da Lighthouse, ma dal Chrome User Experience Report, cioè dai browser degli utenti reali. Quelli con il cookie. Quelli sulla variante normale. È esattamente per questo che il 100 di PageSpeed Insights e il rosso nella sezione “Scopri cosa provano i tuoi utenti reali” della stessa pagina convivono così spesso sui siti LiteSpeed.
Non è LiteSpeed Web Server il problema
Va fatta anche un’altra distinzione.
LiteSpeed Web Server è un prodotto differente dal plugin LiteSpeed Cache.
Il caching server-side di LiteSpeed può essere estremamente efficace esattamente come lo è FastCGI Cache o Proxy Cache su NGINX o Varnish Cache se parliamo di Full Page Cache pure.
Servire HTML già pronto evitando l’avvio completo di WordPress, PHP e database è una tecnica eccellente.
Non è questo ciò che stiamo criticando.
Allo stesso modo non stiamo sostenendo che minificazione, UCSS, Critical CSS, lazy load, defer o delay JavaScript siano tecniche scorrette.
Sono strumenti perfettamente legittimi quando il miglioramento viene applicato all’esperienza che vogliamo realmente fornire agli utenti.
Quello che contestiamo è l’utilizzo di una variante riconoscibile dal tester che permette al benchmark di osservare condizioni significativamente differenti.
C’è tuttavia un dettaglio non indifferente da tenere in considerazione, nel momento in cui si vuole sfruttare appieno il webserver LiteSpeed, si necessita di “comunicare” con il webserver per il purge della cache lato server ed a quel punto il plugin LSCache non è più un’opzione facoltativa, una scelta discrezionale dell’utente, ma un obbligo e l’unica scelta possibile, ed ecco qua che anche a voler sforzarsi di assolvere le intenzioni di LiteSpeed (webserver) separandolo dal plugin LSCache, ricordiamoci sempre che la “combo” è voluta dalla stessa azienda commerciale che sviluppa e VENDE entrambi con sistemi in grado di barare i test facendoli percepire come migliori rispetto ad altre tecnologie, e l’utente non esperto purtroppo ci crede.
La prova è nel sorgente, non nelle nostre opinioni
La cosa che rende questa analisi particolarmente difficile da liquidare come semplice polemica è che tutti i passaggi fondamentali sono visibili nel repository ufficiale, versione 7.9.1.
Abbiamo:
- una lista UA con
Lighthouse,Google,GTmetrixeHeadlessChrome(data/gm_uas.txt); - una lista IP con i range di Google e GTmetrix (
data/gm_ips.txt); - dalla 7.7, un cron che scarica entrambe le liste da
wpapi.quic.cloudcon priorità sul file locale, e la cancellazione delle opzioni corrispondenti dal pannello; - un metodo
always_guest()che confronta User-Agent e indirizzi IP; - una decisione lato WordPress basata solo sull’assenza del cookie
_lscache_vary; - un ramo in
guest.vary.phpche ai tester non consegna il cookie e non ordina il reload; - un reload forzato per tutti gli altri, con una patch a
document.referrerper non rompere Analytics; - Guest Optimization che forza
cfg_js_defer = 2, rimuove Google Fonts enoscript, e ignora URI Excludes e Role Excludes; - preset ufficiali che attivano Guest Optimization lasciando il JavaScript a
1per gli utenti; - script riscritti come
type="litespeed/javascript", URL spostati dasrcadata-src, iframe indata-litespeed-src; - la stringa
DOMContentLoadedsostituita dentro i file JavaScript; - un loader che aspetta eventi come click, mouseover, wheel o touch prima di caricarli, con il timer di fallback commentato.
Non serve immaginare cosa faccia il software.
Il software ce lo dice.
Conclusioni: PageSpeed vede il banco prova, l’utente vede la strada
Il problema di Guest Optimization non è che LiteSpeed sappia ottimizzare una pagina.
Anzi.
Il fatto che riesca a minificare, combinare, ritardare JavaScript, produrre Critical CSS e ridurre il lavoro iniziale dimostra che possiede strumenti tecnicamente interessanti.
Il problema è un altro.
Il sistema è progettato per riconoscere determinate richieste di test e mantenerle in una condizione particolarmente favorevole.
In quella condizione Guest Optimization forza il delay JavaScript.
I normali script esterni vengono trasformati in elementi che il browser non carica immediatamente.
I loro URL vengono parcheggiati in data-src.
La successiva esecuzione dipende da un loader attivato dall’interazione, senza alcun fallback temporale.
E durante un audit automatico quella interazione semplicemente non avviene.
Il risultato è che PageSpeed Insights giudica una pagina sulla quale il costo reale di una parte consistente del JavaScript non è ancora entrato in gioco.
Il browser dell’utente, invece, deve prima o poi pagarne il conto. Anzi, alla prima visita lo paga due volte.
Ecco perché possiamo trovarci con 100 su PageSpeed Insights e 77 su Lighthouse eseguito in Chrome.
Ed ecco perché quel 100 non dovrebbe rassicurarci.
Dovrebbe insospettirci.
Nel Dieselgate la centralina riconosceva il banco prova e cambiava mappatura.
Qui un plugin riconosce caratteristiche del tester e cambia la modalità con la quale viene caricata una parte fondamentale della pagina.
Le conseguenze non sono naturalmente comparabili sul piano legale, ambientale o economico.
Ma il principio del benchmark alterato perché il software sa di essere osservato è sorprendentemente simile.
E il danno, nel nostro settore, è subdolo.
Non è soltanto ricevere un numero discutibile.
È convincersi che non esista più nulla da ottimizzare.
È smettere di cercare quel JavaScript pesante.
È lasciare quell’LCP a quattro secondi.
È non intervenire sul tema, sui plugin e sulle terze parti.
È mostrare al cliente un 100 e chiamarlo performance.
Per noi la regola dovrebbe essere molto più semplice.
Un sito veloce non dovrebbe avere bisogno di riconoscere chi lo sta testando.
Dovrebbe essere veloce per Google PageSpeed Insights.
Dovrebbe essere veloce per Lighthouse.
Ma soprattutto dovrebbe essere veloce per una persona vera che apre Chrome dal proprio telefono.
Se il banco prova e la strada raccontano due storie differenti, noi preferiamo credere alla strada.
E tutti i venditori di Hosting che pur sapendo tacciono, sono correi di un comportamento che in qualsiasi altro settore sarebbe finito in prima pagina per truffa.



