
⚡ Come aggiungere defer e async per gli script WordPress in function.php
Le pagine si caricano lentamente, Google PageSpeed Insights mostra avvisi arancioni e il cliente chiede: «perché il sito è lento?». Nove volte su dieci la causa principale è il JavaScript che blocca il rendering. Il browser incontra un <script>, interrompe la costruzione del DOM, carica ed esegue lo script e solo dopo riprende. In un sito moderno con una dozzina di plugin questo ritardo si trasforma in secondi.
WordPress per molto tempo non ha offerto un modo standard per controllare il caricamento degli script. Gli sviluppatori si destreggiavano tra workaround: filtrare script_loader_tag, modificare l'output tramite clean_url o persino scrivere walker personalizzati per WP_Scripts. Ma con il rilascio di WordPress 6.3 la situazione è cambiata radicalmente e ora abbiamo un modo pulito e supportato per aggiungere defer o async a qualsiasi script senza un solo hack.
Di seguito trovi due metodi funzionanti: l'approccio nativo moderno (WP 6.3+) e il collaudato filtro script_loader_tag (WP 4.1+). Entrambi sono stati testati su progetti reali, entrambi preservano l'integrità della coda delle dipendenze.
💡 Panoramica rapida:
- Comprendi la differenza tra
defereasynce quando usare ciascuno, questo determina se le funzionalità si rompono dopo l'ottimizzazione - Usa il metodo nativo di WordPress 6.3+ tramite
wp_enqueue_script()con il parametrostrategy, l'approccio più pulito che preserva l'ordine di esecuzione - Se il sito gira su una versione inferiore alla 6.3, applica il filtro
script_loader_tagcon un array di handle, questo funziona a partire da WordPress 4.1 - Per script multipli raccogli gli handle in un array e itera con un ciclo, un solo filtro per tutti gli script invece di fare copia-incolla
Cosa sono defer e async e quando usarli
Quando un browser incontra un tag <script> normale, fa tre cose in sequenza: interrompe il parsing dell'HTML, carica lo script, lo esegue. Solo dopo torna all'HTML. In una pagina con cinque script nell'<head> questo significa che l'utente vede una schermata bianca mentre l'ultimo plugin dei commenti si carica, anche se il post stesso avrebbe potuto essere renderizzato molto prima.
Gli attributi defer e async risolvono questo problema, ma funzionano in modo diverso:
Attributo | Quando carica | Quando esegue | Ordine di esecuzione |
|---|---|---|---|
(nessuno) | Blocca subito il parsing | Subito dopo il caricamento | In ordine DOM |
| In parallelo al parsing | Dopo il caricamento completo del DOM | In ordine DOM |
| In parallelo al parsing | Subito dopo il caricamento | Chi carica per primo |
Defer è il cavallo di battaglia per la maggior parte degli scenari. Lo script si carica in parallelo all'HTML e viene eseguito solo quando il DOM è completamente costruito. L'ordine è preservato: lo script A verrà eseguito prima dello script B, anche se B ha caricato più velocemente. Questo è critico per jQuery e tutto ciò che ne dipende.
Async è uno strumento per script indipendenti. Analytics, pubblicità, widget dei social media: non hanno bisogno del DOM, non si preoccupano dell'ordine, devono solo essere eseguiti il prima possibile. Ma se metti async su uno script che dipende da jQuery, probabilmente otterrai $ is not defined.
Regola semplice: lo script dipende da altri script o dal DOM → defer. Lo script è completamente autonomo → async. Nel dubbio, inizia sempre con defer.
Metodo 1: approccio nativo WordPress 6.3+
Da luglio 2023 un nuovo meccanismo è attivo nel core di WordPress. Le funzioni wp_register_script() e wp_enqueue_script() hanno ricevuto un quinto parametro sovraccaricato $args, un array dove puoi specificare la strategia di caricamento. Niente filtri, niente magie con le stringhe, nessun rischio di rompere l'ordine delle dipendenze.
Sintassi di base per defer:
1 wp_enqueue_script( 2 'my-js-handle', 3 get_template_directory_uri() . '/js/my-script.js', 4 array('jquery'), 5 '1.0.0', 6 array( 7 'strategy' => 'defer', 8 'in_footer' => true, 9 ) 10 );
Per async, stessa meccanica:
1 wp_enqueue_script( 2 'google-analytics', 3 'https://www.googletagmanager.com/gtag/js?id=G-XXXXXXXXXX', 4 array(), 5 '1.0.0', 6 array( 7 'strategy' => 'async', 8 'in_footer' => false, 9 ) 10 );
La chiave in_footer dentro l'array funziona come il vecchio parametro booleano: true mette lo script nel footer, false nell'<head>. Per defer tipicamente imposti true (lo script aspetta comunque il DOM, non ha senso caricarlo prima), per async come preferisci.
Il vantaggio principale del metodo nativo è che il core stesso verifica l'albero delle dipendenze. Se lo script A con defer dipende dallo script B, e B è registrato senza strategia (bloccante), WordPress non romperà il sito: declasserà automaticamente la strategia dello script A a bloccante. Usando script_loader_tag non hai questa protezione, il filtro inserisce semplicemente l'attributo senza guardare le dipendenze.
Importante: l'array $args è apparso in WordPress 6.3. Se un tema o un plugin deve funzionare su versioni inferiori, usa il metodo 2 o aggiungi un controllo:
1 if ( version_compare( $GLOBALS['wp_version'], '6.3', '>=' ) ) { 2 // native method 3 } else { 4 // script_loader_tag filter 5 }
Metodo 2: filtro script_loader_tag (WordPress 4.1+)
Se il sito gira su una versione inferiore alla 6.3 o devi mantenere la retrocompatibilità, applica il collaudato filtro script_loader_tag. Esiste da WordPress 4.1 e funziona ancora perfettamente.
Il filtro si attiva subito prima che il tag <script> venga inviato all'HTML, ricevi la stringa del tag pronta, l'handle dello script e il percorso del file, e puoi sostituire src con defer="defer" src o async="async" src.
Script singolo con defer:
1 function add_defer_to_my_script($tag, $handle) { 2 if ( 'my-js-handle' !== $handle ) { 3 return $tag; 4 } 5 return str_replace( ' src', ' defer="defer" src', $tag ); 6 } 7 add_filter('script_loader_tag', 'add_defer_to_my_script', 10, 2);
Il codice va nel functions.php del tema attivo o, più correttamente, in un plugin snippet separato come Code Snippets o WPCode. Se lo metti nel functions.php di un tema child, quando cambi tema gli script torneranno a essere bloccanti e non te ne accorgerai subito.
L'handle dello script è il primo parametro che hai passato a wp_register_script() o wp_enqueue_script(). È ciò che appare nella condizione if. Non tirare a indovinare l'handle, apri il codice sorgente del plugin o del tema e trova la chiamata wp_enqueue_script.
Defer e async per script multipli
Aggiungere un filtro per script è la strada per un functions.php gonfio ed errori di copia-incolla. La soluzione giusta: un array di handle e un solo filtro con un ciclo.
1 function add_defer_to_scripts($tag, $handle) { 2 $scripts_to_defer = array( 3 'my-js-handle', 4 'another-handle', 5 'third-party-lib', 6 ); 7 8 foreach ( $scripts_to_defer as $defer_script ) { 9 if ( $defer_script === $handle ) { 10 return str_replace( ' src', ' defer="defer" src', $tag ); 11 } 12 } 13 return $tag; 14 } 15 add_filter('script_loader_tag', 'add_defer_to_scripts', 10, 2);
Per async, cambiano solo l'attributo e il nome dell'array:
1 function add_async_to_scripts($tag, $handle) { 2 $scripts_to_async = array( 3 'google-tag-manager', 4 'facebook-pixel', 5 'hotjar', 6 ); 7 8 foreach ( $scripts_to_async as $async_script ) { 9 if ( $async_script === $handle ) { 10 return str_replace( ' src', ' async="async" src', $tag ); 11 } 12 } 13 return $tag; 14 } 15 add_filter('script_loader_tag', 'add_async_to_scripts', 10, 2);
Entrambi i filtri possono essere agganciati simultaneamente, defer sui tuoi script, async sui tracker di terze parti. Funzionano indipendentemente e non entrano in conflitto.
Esempio pratico: Google Maps API
Google Maps è un candidato classico per defer. La mappa è tipicamente nel footer della pagina contatti, lo script scarica oltre 100 KB e l'utente non ha bisogno della mappa immediatamente. Inoltre, l'API stessa non dipende da altri script della pagina, un caso ideale.
Connessione e defer:
1 // theme's functions.php 2 function enqueue_google_maps() { 3 if ( ! is_page('contacts') ) { 4 return; 5 } 6 7 wp_enqueue_script( 8 'google-maps-api', 9 'https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY', 10 array(), 11 null, 12 array( 13 'strategy' => 'defer', 14 'in_footer' => true, 15 ) 16 ); 17 } 18 add_action('wp_enqueue_scripts', 'enqueue_google_maps');
Stesso risultato tramite script_loader_tag:
1 function add_defer_to_google_maps($tag, $handle) { 2 if ( 'google-maps-api' !== $handle ) { 3 return $tag; 4 } 5 return str_replace( ' src', ' defer="defer" src', $tag ); 6 } 7 add_filter('script_loader_tag', 'add_defer_to_google_maps', 10, 2);
Dopo aver installato una delle due varianti, verifica assolutamente la mappa nella pagina contatti. Apri la console del browser (F12), assicurati che non ci siano errori JavaScript e che la mappa sia renderizzata correttamente. Se ottieni un errore come initMap is not a function, significa che anche il tuo script di inizializzazione deve essere marcato come defer e posizionato rigorosamente dopo la connessione all'API.
Come verificare che defer e async funzionino
Dopo l'implementazione viene la verifica. Senza non sai se l'ottimizzazione ha funzionato o è solo codice morto.
Apri il sorgente della pagina (Ctrl+U) e trova i tuoi script. Il tag <script> dovrebbe avere gli attributi:
1 <script defer="defer" src="/wp-content/themes/my-theme/js/my-script.js"></script>
Se non ci sono attributi, controlla se l'handle nel filtro corrisponde all'handle reale dello script. Errore comune: in wp_enqueue_script l'handle è my-plugin-frontend, ma nel filtro è my_plugin_frontend. Trattino contro underscore, e il filtro salta silenziosamente lo script.
Tocco finale, Google PageSpeed Insights o Lighthouse nella scheda Audits degli strumenti per sviluppatori. La sezione «Elimina le risorse che bloccano il rendering» dovrebbe mostrare un miglioramento. Il guadagno specifico dipende dal numero e dalla dimensione degli script, ma per un tipico sito WordPress con 5-7 plugin una riduzione del 40-60% del JavaScript bloccante è un risultato raggiungibile.
⁉️🤔 Domande frequenti
Posso usare sia defer che async su uno script?
No. Se specifichi entrambi gli attributi contemporaneamente, il browser ignorerà
defered eseguirà lo script comeasync. Questo comportamento è previsto dalle specifiche HTML,asyncha sempre la priorità. Scegline uno in base all'importanza dell'ordine di esecuzione.
Cosa fare se dopo aver aggiunto defer lo script smette di funzionare?
Molto probabilmente lo script si aspetta che il DOM non sia ancora stato costruito e cerca di manipolare elementi che non esistono al momento dell'esecuzione. Sostituisci
defercon il caricamento bloccante standard per quello script specifico. Oppure avvolgi il codice dello script inDOMContentLoaded, così potrà funzionare condefersenza errori. La seconda opzione è preferibile: mantieni l'ottimizzazione e risolvi la compatibilità.
Qual è la differenza tra defer e spostare lo script nel footer tramite wp_enqueue_script con $in_footer = true?
$in_footer = truesposta semplicemente il tag<script>dall'<head>alla fine del<body>. Lo script blocca ancora il rendering, solo più tardi.defercarica in parallelo al parsing HTML ed esegue rigorosamente dopo che il DOM è costruito. L'uso combinato (in_footer => true+strategy => 'defer') dà il massimo effetto: lo script nel footer non ritarda il primo rendering e defer garantisce che non blocchi nemmeno il rendering finale.
Dovrei aggiornare WordPress alla 6.3 solo per il metodo nativo?
Se il sito è sulla versione 6.2 o precedente, vale la pena aggiornare non solo per
strategy. WordPress 6.3 ha chiuso dozzine di vulnerabilità e ha portato miglioramenti delle prestazioni del core. Ma se un aggiornamento è impossibile per qualche motivo, il filtroscript_loader_tagfunziona in modo assolutamente affidabile dalla versione 4.1, rilasciata nel 2014. Non perdi nulla a usarlo.
E jQuery, defer o lo lascio così com'è?
jQuery dovrebbe caricarsi con
deferse tutti gli script dipendenti sono anch'essi marcatidefer. Il problema è che i plugin WordPress gestiscono molto raramente gli attributi per i loro script. Se mettidefersu jQuery mentre un plugin di contact form collega il suo script senza attributi, il browser eseguirà il plugin prima di jQuery e il form si romperà. Consiglio pratico: inizia condeferper gli script del tuo tema. Non toccare jQuery finché non hai testato ogni plugin del sito.
Cosa mettere su un sito in produzione nel 2026
Se il server esegue WordPress 6.3 o superiore, solo il metodo nativo. Codice pulito, protezione dai conflitti di dipendenza, supporto del core. Inizia con defer per tutti gli script del tema e i plugin criticamente importanti; riserva async per analytics e widget di terze parti.
Se la versione è inferiore alla 6.3, il filtro script_loader_tag con un array di handle. Ha funzionato per un decennio, niente da rompere. L'unica cosa che non può fare è controllare automaticamente l'albero delle dipendenze, quindi aggiungi gli script all'array uno alla volta e controlla il sito dopo ciascuno.
E soprattutto: nessun metodo sostituisce l'audit degli script stessi. Se un plugin galleria collega 15 file solo per mostrare tre immagini, né defer né async aiuteranno radicalmente. L'ottimizzazione del caricamento inizia con la domanda «questo script è davvero necessario» e solo dopo «come caricarlo».
🔗 Documentazione ufficiale WordPress 6.3, Strategie di caricamento degli script



