Il modulo mod_pagespeed, per web server Apache, è nato con l'obiettivo di migliorare le prestazioni dei siti web, rendendo automatico il processo di ottimizzazione di pagine e risorse. Codice HTML, JavaScript e CSS, così come immagini JPEG o PNG, vengono elaborati tramite particolari filtri che implementano alcune best practices note per il loro impatto sulla velocità di risposta delle pagine web.
Prima di analizzare il modulo e fornire esempi pratici di configurazione, chiariamo i principi a chi non abbia dimestichezza con l'argomento. Le discussioni e gli studi sul miglioramento delle prestazioni dei siti web hanno dato vita a raccolte di regole (best practices) che consentono di affrontare con metodo un eventuale processo di ottimizzazione. Tale operazione può risultare poco gravosa se applicata dall'inizio allo sviluppo di un nuovo sito. Più impegnativa, invece, nel caso di prodotti già esistenti che debbano essere perciò rielaborati. La novità introdotta da mod_pagespeed è che un sistema automatico possa sostituire in buona parte l'attività manuale, con risparmio di tempo e fatica.
Avevamo affrontato il discorso delle best practices in un precedente articolo che aveva proprio lo scopo di aprire la strada alle considerazioni riportate in queste pagine. In particolare ci eravamo riferiti alle Web Performance Best Practices di Google.
Non a caso mod_pagespeed si sviluppa all'interno del più ampio progetto Page Speed targato Google. I componenti della famiglia sono:
- L'estensione Page Speed: disponibile per i browser Chrome e Firefox, che consente di valutare le prestazioni delle pagine web semplicemente visualizzandole con il proprio navigatore.
- Page Speed Online: un servizio online, con funzionalità analoghe all'estensione, che non richiede alcuna installazione ed è quindi utilizzabile con qualunque browser. Da segnalare il mobile report specifico per i dispositivi mobili.
- Il modulo per Apache mod_pagespeed, oggetto dell'articolo: i webmaster possono velocizzare le proprie pagine senza dover intervenire manualmente sui problemi evidenziati dagli algoritmi di Page Speed.
- Page Speed Service: un servizio online (una sorta di proxy) che riceve le pagine dal nostro server, le riscrive seguendo le regole delle best practices e le fornisce ai navigatori attraverso i server di Google.
Vediamo allora come installare e configurare questo modulo open source, prendendo come riferimento un server Linux con sistema operativo Fedora 15 a 64 bit e web server Apache 2.2.21.
Installazione
Innanzitutto va rilevato che il progetto supporta solamente Apache 2.2 e non precedenti versioni. I test vengono effettuati su due distribuzioni Linux: CentOS e Ubuntu. Questo ci porta ragionevolmente a credere che il tutto funzioni anche su altre distribuzioni derivate da Debian o RedHat. Il software è disponibile per sistemi i386 e x86-64, sia pacchettizzato che in forma di codice sorgente. Per l'installazione dai sorgenti, più laboriosa, vi rimando alla documentazione ufficiale. Di seguito seguiremo la comoda via dell'installazione da rpm.
La pagina di download si riferisce a tutti i componenti Page Speed, dobbiamo dunque scorrerla fino alla dicitura mod_pagespeed for Apache. Dall'elenco dei pacchetti .deb e .rpm preleviamo la versione adatta al sistema operativo in uso, nel nostro caso mod_pagespeed 64-bit .rpm (CentOS/Fedora). Non sorprendetevi se il nome del file scaricato, mod-pagespeed-beta_current_x86_64.rpm, riporta la parola "beta", il modulo è infatti ancora giovane per quanto sufficientemente stabile.
A questo punto assumiamo i privilegi di root, spostiamoci nella directory di lavoro in cui abbiamo salvato il file e procediamo all'installazione:
$ rpm -ivh mod-pagespeed-beta_current_x86_64.rpm
Verifichiamo subito gli effetti dell'operazione: innanzitutto in /usr/lib64/httpd/modules noteremo, accanto agli altri moduli, anche mod_pagespeed.so. Nella directory /etc/httpd/conf.d troveremo il file pagespeed.conf su cui agire per modificare i parametri di configurazione predefiniti. Infine in /var/www balza all'occhio la creazione di due nuove directory funzionali al modulo: mod_pagespeed e mod_pagespeedcache.
Il modulo è già attivo e funzionante sarà necessario personalizzarlo per ottenere il massimo dalle sue potenzialità.
Configurazione
Come già accennato il centro di controllo risiede nel file /etc/httpd/conf.d/pagespeed.conf. La prima direttiva che incontriamo al suo interno consente di caricare il modulo dinamicamente:
LoadModule pagespeed_module /usr/lib64/httpd/modules/mod_pagespeed.so
subito dopo viene attivato mod_deflate, se non è già all'opera:
<IfModule !mod_deflate.c> LoadModule deflate_module /usr/lib64/httpd/modules/mod_deflate.so </IfModule>
Ricordo brevemente che mod_deflate (ex mod_gzip per Apache 1.3) serve a comprimere le informazioni fornite al browser riducendo le dimensioni dei dati scambiati. Per approfondimenti potete consultare l'articolo Risparmiare banda con il modulo deflate di apache.
Finalmente all'interno del contenitore <IfModule pagespeed_module>[...]</IfModule>
troviamo le direttive che consentono di personalizzare il funzionamento del modulo. Più che analizzare le configurazioni ad una ad una, è meglio inquadrarle logicamente ed analizzarne alcune a titolo esemplificativo. Del resto il file pagespeed.conf risulta abbondantemente commentato e la documentazione on line, in stile Google, è completa e dettagliata.
Come già anticipato mod_pagespeed filtra i contenuti richiesti dal browser del visitatore. In tal modo essi arriveranno al destinatario ottimizzati secondo i criteri delle best practices. Alcuni filtri si limitano ad alterare il codice HTML, ad esempio eliminando gli spazi in eccesso. Altri modificano le risorse producendone una versione migliorata che sostituirà l'originale.
I filtri possono essere singolarmente abilitati o disabilitati, alcuni infatti sono attivi di default. In base all'azione svolta risultano raggruppati in categorie, che poi rispecchiano l'analoga suddivisione delle Web Performance Best Practices di Google:
- Ottimizzazione del sistema di caching: il riferimento è alla memorizzazione locale di risorse operata da browser e web proxy server, per evitare di prelevare ad ogni visita elementi che raramente subiscono aggiornamenti (immagini, fogli di stile, script, file swf...). La funzione dei filtri di questa categoria è sfruttare al meglio il meccanismo della cache per ridurre la quantità di dati prelevata dal server, con conseguente risparmio di banda. Inoltre, poiché il client punterà a risorse salvate localmente anziché remote, la visualizzazione delle pagine risulterà notevolmente velocizzata.
- Minimizzazione degli RTT (Round Trip Times): con RTT si intende il tempo necessario perché il client invii una richiesta ed il server risponda, senza considerare il tempo necessario a trasferire la risorsa. Una pagina web può richiedere molti RTT. Ad esempio alla prima connessione di un browser sarà necessario un RTT per la risoluzione DNS del nome, uno per l'inizializzazione della connessione TCP, ed infine svariati RTT per le richieste e risposte HTTP. Poiché queste ultime costituiscono la maggioranza, ridurne il numero o parallelizzarle incrementerà la velocità di risposta delle pagine, specie nei casi in cui i file trasferiti siano di piccole dimensioni.
- Minimizzazione dei dati in eccesso nelle richieste HTTP: l'esempio più classico sono i cookie le cui informazioni vengono comunicate tramite le intestazioni HTTP. Il client invierà il suo fardello di dati ad ogni richiesta, ma se si tratta ad esempio di un'immagine ciò ha poco senso. Specie per elementi di piccole dimensioni le informazioni trasferite con gli header possono essere determinanti nell'aumento dei tempi di risposta. Come già evidenziato nel precedente articolo, la creazione di un dominio cookie-free per ospitare questo tipo di risorse può essere una scelta appropriata. Mod_pagespeed, con la collaborazione dell'amministratore di sistema, può facilitare tale migrazione.
- Minimizzazione delle dimensioni dell'informazione trasmessa: forse questo è l'aspetto più evidente e immediato, meno dati trasferiamo minor latenza avremo nel disegno della pagina. Oltre a ciò si potrebbero azzardare altre considerazioni legate alle dimensioni massime prefissate per i pacchetti trasmessi attraverso la rete (MTU). Mantenendo contenute le dimensioni dei file evitiamo la suddivisione delle informazioni in più pacchetti e riduciamo il numero complessivamente trasmesso dal server. I filtri che rientrano in questo gruppo operano, ad esempio, la minimizzazione di script e fogli di stile, o l'eliminazione di spazi inutili nel codice HTML.
- Ottimizzazione del rendering del browser: fornire i contenuti in modo appropriato può velocizzare le operazioni necessarie al programma di navigazione per visualizzare una pagina. Ad esempio specificando le dimensioni di un'immagine, o del corrispondente blocco contenitore, si evita un nuovo passaggio ed aggiustamento nel disegno una volta che il file sia stato scaricato. Anche le regole CSS hanno un loro impatto costringendo il browser ad un attraversamento più o meno efficiente del DOM. Un'istruzione del tipo
ul li a {...}
risulta molto inefficiente. Primo perché un tag, elemento molto generico, viene usato come selettore. Secondo perché il navigatore dovrà risalire l'albero per valutare ogni progenitore. Molto meglio in questa particolare ottica, utilizzare una classe o un id. L'azione dei filtri di questa categoria tende a semplificare il lavoro del navigatore per esempio con l'attribuzione automatica delle dimensioni ai tag<img>
.
Dopo tante considerazioni teoriche passiamo ora ad una configurazione pratica. La vedremo nella prossima pagina che sarà pubblicata il prossimo lunedì.
Esempi di configurazione
Vediamo alcune direttive del modulo, iniziando con la sua attivazione o disattivazione (on
/off
):
ModPagespeed on
Per migliorare la permanenza in cache delle risorse abilitiamo il seguente filtro:
ModPagespeedEnableFilters extend_cache
Le azioni svolte sono sostanzialmente due: innanzitutto una modifica degli header HTTP con un max-age=31536000 che porta ad un anno il tempo di vita della copia locale. In secondo luogo viene operata una riscrittura degli URL che referenziano le risorse cui viene attribuito un codice univoco mediante una funzione di hash. Ad esempio un'immagine che inizialmente nel codice HTML era richiamata così:
<img src="/ima/logo_sito.png" />
diviene:
<img src="ima/xlogo_sito.png.pagespeed.ic.uxlq61TFfz.png" width="173" height="97"/>
Il modulo utilizzerà il valore originariamente specificato con max-age
per controllare periodicamente la risorsa e verificare se sia stata modificata. In tal caso le attribuirà un nuovo codice hash modificandone il nome. Per il browser del navigatore risulterà diversa e quindi la preleverà nuovamente dal server. Nell'esempio si può notare anche l'attribuzione delle dimensioni al tag <img>
: questo filtro è abilitato di default.
La funzione di estensione della cache è parte integrante anche di altri filtri che nella loro azione riscrivono gli URL delle risorse mediante hash.
Un esempio di direttiva utilizzata per ridurre il numero di richieste HTTP è:
ModPagespeedEnableFilters combine_css
Supponendo che nella nostra pagina vengano richiamati vari file CSS esterni:
<link href="/css/form.css" media="screen" rel="stylesheet" type="text/css" /> <link href="/css/admin.css" media="screen" rel="stylesheet" type="text/css" />
l'azione del filtro opererà questa trasformazione del codice:
<link href="/css/form.css+admin.css.pagespeed.cc.y_GFiCdG84.css" media="screen" rel="stylesheet" type="text/css"/>
cui corrisponderà la creazione di un unico file CSS che riassumerà il contenuto di tutti gli altri.
Abbiamo parlato di parallelizzazione al fine di ridurre il numero di round trip time. Ciò si può ottenere suddividendo il download tra più hostname, anche se in realtà si riferiscono al medesimo server:
ModPagespeedShardDomain www.miosito.it static1.miosito.it,static2.miosito.it
nell'agire sull'hostname www.miosito.it mod_pagespeed suddividerà gli URL su cui effettua riscritture tra static1.miosito.it e static2.miosito.it. Ad esempio il risultato potrà essere:
<link rel="stylesheet" type="text/css" media="screen" href="http://static2.miosito.it/css/default.css,Mcf.tMJWVe8dnX.css.pagespeed.ce.tMJWVe8dnX.css"/> <img src="http://static1.miosito.it/images/xworld_map_white_light.png.pagespeed.ic.ObCptmGWmk.png" alt="world" width="157" height="91"/>
Il numero di host specificabili, che comunque devono essere presenti e raggiungibili, è teoricamente infinito, ma allo stato attuale conviene mantenersi tra i due e i quattro al massimo.
Il filtro successivo ha lo scopo di ottimizzare le immagini con varie azioni:
ModPagespeedEnableFilters rewrite_images
Se abbiamo abilitato rewrite_css
oltre ad agire sui tag <img>
andrà ad intervenire sulle proprietà CSS background-image
, background
, list-style-image
e list-style
. In realtà tale direttiva è una scorciatoia per abilitare più filtri contemporaneamente:
ModPagespeedEnableFilters insert_image_dimensions,inline_images,recompress_images,resize_images
Il primo, insert_image_dimensions
, inserisce gli attributi width=""
and height=""
, se mancanti, con i valori corrispondenti all'immagine. Il secondo, inline_images
, sostituisce i riferimenti a piccole immagini con la codifica inline in base 64, eliminando la necessità di una connessione HTTP per scaricarle:
<img src="data:image/png;base64,iVBORw0KGgoAAAA........BJRU5ErkJggg==" alt="mia immagine">
Il filtro recompress_images
, ricomprime le immagini ed elimina i metadati inutili, la sostituzione dell'originale viene fatta solo se il risultato è migliorativo. Inoltre sostituisce i formati gif con png. Da ultimo resize_images
riduce le immagini le cui dimensioni effettive siano maggiori di quelle richiamate con gli attributi width=""
e height=""
. Anche in questo caso la sostituzione avviene solo se il file risulta alleggerito. Entrambi i filtri ricorrono alle librerie libjpeg e libpng.
Conclusioni
In queste pagine penso di aver dato un "assaggio" delle potenzialità di mod_pagespeed, che possono essere approfondite sul sito del progetto. Pur automatizzando il processo di ottimizzazione il modulo richiede un minimo di cognizioni sugli argomenti trattati, a meno di non (sotto)utilizzarlo con le impostazioni di default. Anche in questo caso ci possono essere però delle sorprese, perché ciascun filtro ha associato un rischio basso, moderato o alto. Immaginate di utilizzare un codice JavaScript che elabori un'immagine sulla base del nome, la riscrittura degli URL pur mantenendo inalterato il file ne modificherà il nome, generando un errore di esecuzione. Il mio consiglio è dedicare un po' di applicazione allo studio delle basi teoriche dell'ottimizzazione per poi spingere più a fondo l'acceleratore nell'applicazione dei filtri.