Il TTFB, il tempo che passa tra la richiesta di una pagina e l’arrivo del primo byte della risposta, è uno dei valori più importanti per le prestazioni, perché ritarda tutto ciò che segue, a partire dall’LCP. Ma dal browser il TTFB appare come un unico blocco di attesa: non dice se il tempo è stato speso nella rete, nella CDN, nell’avvio dell’applicazione, nelle query al database o nella generazione del template. Per saperlo bisogna guardare dentro il server, con strumenti che di solito vedono solo gli sviluppatori e che non sono collegati all’esperienza dei visitatori.
L’intestazione HTTP Server-Timing risolve questo problema in modo semplice: il server comunica al browser, insieme alla risposta, come ha speso il tempo. I DevTools la mostrano, gli script di misura la leggono, e i sistemi di monitoraggio possono raccoglierla dagli utenti reali. In questo articolo vediamo come funziona, cosa conviene misurare e come usarla in WordPress.
Come funziona
Server-Timing è un’intestazione di risposta che contiene una o più voci. Ogni voce ha un nome e, facoltativamente, una durata in millisecondi e una descrizione. I nomi sono liberi: ogni applicazione decide cosa misurare.
Server-Timing: cache;desc="MISS", boot;dur=40, db;dur=180;desc="Database", tpl;dur=90, total;dur=322
L’intestazione non cambia nulla nel comportamento della pagina: è un’informazione in più, che il browser rende disponibile. Può essere aggiunta dall’applicazione, dal server web, da un proxy o dalla CDN, e più livelli possono aggiungere le proprie voci alla stessa risposta.
Dove si vede
Nei DevTools. Nel pannello Rete, selezionando la richiesta della pagina e aprendo la scheda dei tempi, le voci di Server-Timing compaiono in una sezione dedicata, con le loro durate rappresentate come barre. È il modo più rapido per capire, durante una prova, perché una pagina ha un TTFB alto: basta confrontarla con il waterfall delle altre richieste.
Negli script. Le voci sono accessibili dal JavaScript della pagina tramite le API di misura del browser, nella proprietà serverTiming della voce di navigazione e delle voci delle risorse:
const [ navigazione ] = performance.getEntriesByType( 'navigation' );
for ( const voce of navigazione.serverTiming ) {
console.log( voce.name, voce.duration, voce.description );
}
Per le risorse provenienti da altre origini, le voci sono esposte agli script solo se la risposta include anche l’intestazione Timing-Allow-Origin; per il documento principale e le risorse della stessa origine sono sempre disponibili, nei browser che supportano l’API.
Il valore vero: i dati reali
Il punto di forza di Server-Timing è che collega il lavoro del server all’esperienza dei visitatori. Un sistema di monitoraggio degli utenti reali può leggere le voci per ogni visita e inviarle insieme al TTFB e all’LCP. Si possono così rispondere a domande che nessun test di laboratorio risolve: quale quota delle visite reali trova la pagina in cache? Quanto è più lento il TTFB quando la pagina viene generata? Quali pagine hanno le query più lente nelle condizioni reali di carico?
L’esempio più utile è lo stato della cache. Una pagina servita dalla cache di pagina ha un TTFB di poche decine o centinaia di millisecondi; una generata da zero, anche di un secondo o più. Se il 75° percentile del TTFB è alto, la domanda è se dipende da troppe pagine generate o da pagine generate troppo lentamente: i rimedi sono diversi. Nel primo caso si lavora sulla configurazione della cache, per esempio sui parametri delle campagne che la aggirano, come visto nell’articolo sulle landing page; nel secondo, sul codice, sul database e sul server.
Cosa misurare
Le voci utili dipendono dall’applicazione, ma alcune sono quasi sempre interessanti:
- lo stato della cache di ciascun livello, CDN e cache di pagina, con valori come HIT e MISS nella descrizione;
- il tempo totale dell’applicazione, dall’inizio della richiesta all’invio della risposta;
- il tempo di avvio, cioè il caricamento del framework e dei plugin prima che inizi il lavoro specifico della pagina;
- il tempo delle query al database, e se possibile il loro numero;
- il tempo delle chiamate a servizi esterni, come API di terze parti o servizi di pagamento, che sono spesso le voci più variabili;
- l’object cache: se è attivo un object cache persistente, la quota di dati trovati in memoria.
Conviene tenere le voci poche e stabili, con nomi brevi: l’intestazione viene inviata con ogni risposta e deve restare leggera, e voci coerenti nel tempo rendono possibile il confronto tra periodi diversi.
In WordPress
In WordPress ci sono due strade. La prima è il plugin Performance Lab, sviluppato dal gruppo di lavoro sulle prestazioni di WordPress, che include un modulo per Server-Timing: aggiunge voci per il tempo prima del template, il tempo del template e il tempo totale, e offre un’interfaccia di programmazione con cui altri plugin e temi possono registrare le proprie misure. Per il tempo del template il modulo usa un buffer dell’output, perché le intestazioni devono essere inviate prima del contenuto.
La seconda strada è aggiungere l’intestazione con poche righe di codice, per misure mirate. L’esempio seguente comunica il tempo trascorso dall’inizio della richiesta fino alla scelta del template, cioè la fase in cui WordPress carica plugin e tema ed esegue la query principale, e lo fa solo per gli amministratori:
add_action(
'template_redirect',
function () {
if ( ! current_user_can( 'manage_options' ) || headers_sent() ) {
return;
}
$ms = ( microtime( true ) - $_SERVER['REQUEST_TIME_FLOAT'] ) * 1000;
header( sprintf( 'Server-Timing: wp;dur=%.1f;desc="Fino al template"', $ms ) );
}
);
Una limitazione ai soli amministratori è utile per le diagnosi, ma esclude i dati reali. Per il monitoraggio conviene esporre a tutti poche voci non sensibili, come i tempi complessivi e lo stato della cache, e riservare i dettagli agli utenti autorizzati.
Cache e intestazioni
C’è un’insidia da conoscere. Quando una pagina viene salvata nella cache insieme alle sue intestazioni, anche Server-Timing viene salvata, e le visite successive ricevono le durate misurate al momento della generazione, non quelle attuali. Il risultato sono dati fuorvianti: pagine servite in pochi millisecondi dalla cache che dichiarano centinaia di millisecondi di database. Conviene quindi che il livello di cache aggiunga la propria voce con lo stato, così da poter distinguere i due casi, e che le durate dell’applicazione vengano escluse dalla cache o interpretate solo quando lo stato è MISS. Molte CDN aggiungono automaticamente voci proprie, come lo stato della cache ai bordi e il tempo di risposta dell’origine, e valgono le stesse attenzioni viste nell’articolo su Cloudflare riguardo alla lettura dei dati della CDN.
Non solo la pagina
Server-Timing non è limitata al documento HTML. Qualunque risposta può includerla: le chiamate alle interfacce di programmazione usate dalla pagina, come quelle che caricano i filtri di un negozio o i risultati di una ricerca, le immagini generate al volo da un servizio di trasformazione, i file serviti da una CDN. Per le interazioni che dipendono da una richiesta al server, come applicare un filtro o aggiungere un prodotto al carrello, sapere quanto tempo il server ha impiegato a rispondere aiuta a distinguere un problema di codice nel browser, che si vedrebbe nei dati dei fotogrammi lunghi, da un problema di backend. Nelle applicazioni con più servizi interni, ciascuno può aggiungere la propria voce, e la risposta finale porta con sé la scomposizione completa del percorso.
Dal dato all’intervento
Ogni voce suggerisce una direzione diversa, e questo è il vantaggio pratico della scomposizione:
- molti MISS di cache: si lavora sulla configurazione della cache, cioè parametri ignorati, cookie che la escludono, durate troppo brevi, pagine escluse senza motivo;
- avvio lento: il problema è nei plugin caricati a ogni richiesta, nelle opzioni caricate automaticamente e nella versione di PHP;
- database lento: si cercano le query responsabili con gli strumenti di diagnosi e si interviene su indici, metadati e dati accumulati, come visto nell’articolo sul database di WordPress;
- servizi esterni lenti: si mettono in cache le risposte, si spostano le chiamate fuori dal percorso della pagina o si impostano tempi massimi più brevi;
- tutto nella norma ma TTFB alto: il tempo è nella rete, e i rimedi sono la CDN, la distanza dal server e le connessioni, oppure nelle code del server quando i processi disponibili sono pochi, come discusso nell’articolo sull’hosting.
Gli errori più comuni
Il primo errore è inviare l’intestazione dopo che il contenuto ha già cominciato a essere trasmesso: in PHP le intestazioni devono precedere l’output, e un tentativo tardivo viene ignorato o genera un avviso. Il secondo è misurare con orologi diversi in punti diversi, ottenendo durate che non si sommano. Il terzo è lasciare attive voci di diagnosi molto dettagliate in produzione per tutti, con un aumento del peso delle risposte e il rischio di esporre informazioni. Il quarto, forse il più frequente, è aggiungere l’intestazione e non usarla: il suo valore sta nel raccoglierla dai visitatori reali e nel guardarla quando il TTFB peggiora, non nell’averla configurata una volta. Nel pannello Performance e nel pannello Rete dei DevTools resta comunque uno strumento prezioso per le diagnosi quotidiane.
Sicurezza
Le informazioni in Server-Timing sono visibili a chiunque apra i DevTools. Non devono contenere dati sensibili come nomi di tabelle, query, indirizzi interni o versioni del software. Anche i tempi possono dare indizi, per esempio rivelando se un utente o un prodotto esistono in base alla durata di una query; per le pagine che gestiscono dati riservati, come l’accesso o l’area personale, conviene limitare le voci ai valori complessivi o ometterle.
Un esempio concreto
Un negozio online ha un 75° percentile del TTFB di quasi un secondo e mezzo nei dati reali, mentre le prove in laboratorio sulle stesse pagine danno poche centinaia di millisecondi. Il sito aggiunge tre voci di Server-Timing: lo stato della cache di pagina, il tempo totale di WordPress e il tempo delle chiamate a servizi esterni, e le raccoglie con il sistema di monitoraggio. I dati mostrano che solo metà delle visite alle pagine di prodotto trova la pagina in cache, per via dei parametri aggiunti dalle campagne e di un cookie impostato da un plugin di confronto prodotti, e che le pagine generate spendono in media 400 millisecondi in una chiamata a un servizio di calcolo delle spese di spedizione. Corretta la configurazione della cache e messa in cache per qualche minuto la risposta del servizio esterno, il TTFB reale scende sotto i 600 millisecondi.
In sintesi
Server-Timing è un’intestazione che permette al server di comunicare al browser come ha speso il tempo: stato della cache, avvio, database, template, servizi esterni. I DevTools la mostrano durante le prove, e gli script di misura la leggono, così che i sistemi di monitoraggio possano collegare il lavoro del server al TTFB e all’LCP dei visitatori reali. In WordPress si può aggiungere con il plugin Performance Lab o con poche righe di codice. Va usata con qualche attenzione: voci poche e stabili, nessun dato sensibile, e una voce di stato della cache per non farsi ingannare dalle intestazioni salvate insieme alle pagine. Con queste accortezze, il TTFB smette di essere una scatola chiusa.