Skip to content
EVE Milano Consulenza SEO

EveMilano Logo White EveMilano Logo White

Aggiornato a ottobre 2026. Nel 2013 questo articolo consigliava di caricare jQuery da ajax.googleapis.com invece che dal proprio server. Di quegli argomenti ne resta in piedi uno solo, e solo in parte: da Chrome 150 la cache condivisa tra siti è tornata, ma per un elenco chiuso di URL. Qui trovi lo stato attuale, verificato sul codice sorgente di Chromium e con un test che puoi rieseguire.

In breve

  • Safari e Firefox partizionano la cache HTTP per sito: il jQuery scaricato visitando un altro sito non viene riusato, qualunque CDN tu scelga.
  • Chrome la partiziona da Chrome 86 (ottobre 2020). Da Chrome 150, stabile dal 30 giugno 2026, fa un’eccezione per un elenco di risorse molto diffuse (188 pattern in Chrome 155). Nell’elenco ci sono alcune build minificate di jQuery su ajax.googleapis.com, code.jquery.com e cdnjs, tra cui la 3.7.1.
  • Il vantaggio dell’eccezione è piccolo, come ammette lo stesso autore della funzione: vale solo alla prima visita, solo su Chrome, solo con i cookie di terze parti consentiti e solo se il visitatore ha già in cache lo stesso identico URL, scaricato su un altro sito.
  • Il costo di un cache miss resta: una connessione in più verso un’origine esterna (nel mio test 104 ms di mediana per DNS, TCP e TLS su fibra), la dipendenza da un servizio di terzi e l’invio dell’IP del visitatore a Google.
  • Su WordPress la risposta è no. Il core include jQuery 3.7.1 e jQuery Migrate 3.6.0 in /wp-includes/js/jquery/: lascialo dove è.

Cosa non regge più dell’articolo del 2013

Il post originale elencava tre vantaggi (latenza, parallelismo, cache) e due metodi di implementazione. Punto per punto:

  • Parallelismo. Il limite di due connessioni per host riguardava i browser di allora su HTTP/1.1. Con HTTP/2 e HTTP/3 le richieste verso la stessa origine viaggiano in parallelo sulla stessa connessione: spostare jQuery su un altro host non libera nulla e aggiunge una connessione da aprire.
  • Latenza. Una CDN avvicina i file all’utente, ma il confronto giusto non è “Google contro il tuo server”, è “Google contro la tua CDN”. Se il sito è già dietro una CDN, jQuery arriva dallo stesso edge del resto della pagina e sulla connessione già aperta.
  • Cache tra siti. Era il vantaggio principale ed è sparito con il partizionamento della cache. Su Chrome è tornato in parte: lo vediamo sotto.
  • URL senza protocollo (//ajax.googleapis.com/...). Paul Irish, che nel 2010 ne aveva spiegato il funzionamento, dal 2014 lo definisce un anti-pattern: se la risorsa è disponibile in HTTPS, si usa sempre https://.
  • google.load() e www.google.com/jsapi. Il vecchio loader non esiste più: oggi www.google.com/jsapi risponde con un 301 verso il loader di Google Charts.
  • Lo snippet per functions.php. Sostituiva il jQuery di WordPress con la 1.11.3 caricata in http://: su un sito HTTPS è mixed content, scollega la libreria dalla versione testata con il core e la riporta a una release con vulnerabilità note.
  • Il plugin Use Google Libraries. Ultimo aggiornamento a novembre 2017, testato fino a WordPress 4.7: va disinstallato.
  • jQuery 1.10.2, la versione dello snippet. È esposta almeno a CVE-2015-9251, CVE-2019-11358 e CVE-2020-11023; quest’ultima è nel catalogo CISA delle vulnerabilità sfruttate attivamente dal 23 gennaio 2025.

Come funziona la cache HTTP partizionata

Fino a Chrome 85 la chiave della cache HTTP era il solo URL della risorsa. Se il sito A e il sito B caricavano lo stesso file da ajax.googleapis.com, il secondo lo trovava già in cache: è il “cross-site caching” che descriveva l’articolo originale.

Lo stesso meccanismo però si presta agli attacchi XS-Leaks. Misurando quanto ci mette una risorsa a caricarsi, un sito può capire se l’utente ha visitato un altro sito, dedurre i risultati di una ricerca fatta altrove o usare la cache come identificatore persistente. Per chiudere questi canali i browser hanno aggiunto alla chiave il sito da cui parte la richiesta:

Chrome 85 e precedenti   chiave = URL
Chrome 86 e successivi   chiave = (sito top-level, sito del frame, URL)
Firefox 85 e successivi  chiave = (sito top-level, URL)
Safari                   chiave = (sito top-level, URL)

In Chrome il “sito” è schema più dominio registrabile (eTLD+1): https://shop.esempio.it e https://www.esempio.it condividono la partizione, https://esempio.com no. Chrome ha attivato il partizionamento con la versione 86 e ne ha pubblicato l’impatto: miss rate della cache +3,6%, byte scaricati dalla rete +4% circa, First Contentful Paint +0,3%. Firefox 85 (gennaio 2021) partiziona per sito di primo livello anche la cache DNS, quella dei certificati TLS, la cache delle immagini e le connessioni in pool; Safari partiziona la cache da anni.

L’eccezione di Chrome per le risorse “estremamente diffuse”

A ottobre 2025 Patrick Meenan di Google ha presentato l’intent to ship di Cache sharing for extremely-pervasive resources: per un elenco curato a mano di script, fogli di stile e dizionari di compressione presenti su una larga parte del web, Chrome torna a usare una cache con chiave singola, cioè il solo URL. La scheda su Chrome Platform Status la motiva così: il partizionamento ha risolto i problemi di privacy ma ha avuto un costo in prestazioni, e questa eccezione ne recupera una parte.

Ho ricostruito i tempi leggendo services/network/public/cpp/features.cc nei branch di ogni versione di Chromium: il flag CacheSharingForPervasiveResources compare in Chrome 146 disattivato di default e diventa attivo di default con Chrome 150. Nelle versioni dalla 146 alla 149 Google poteva comunque attivarlo per una parte degli utenti tramite i field trial.

Quali URL di jQuery sono nell’elenco

L’elenco è compilato nel file services/network/pervasive_resources/shared_resource_checker_patterns.h ed è generato da uno script che interroga l’HTTP Archive. Entrano solo le risorse con almeno 100.000 occorrenze al mese per sei mesi consecutivi, risposta 200 con Cache-Control: public, nessun Set-Cookie e nessuna query string. Per jQuery, nella build di Chrome 155:

  • ajax.googleapis.com, solo jquery.min.js: 1.9.1, 1.11.0, 1.11.1, 1.11.3, 1.12.4, 3.2.1, 3.3.1, 3.4.1, 3.5.1, 3.6.0, 3.7.1
  • code.jquery.com: jquery-3.4.1.min.js, jquery-3.5.1.min.js, jquery-3.6.0.min.js, jquery-3.7.1.min.js
  • cdnjs.cloudflare.com: solo ajax/libs/jquery/3.7.1/jquery.min.js

Restano fuori jsDelivr, le build non minificate e jQuery 4.0.0, uscito a gennaio 2026, che Google Hosted Libraries non ospita nemmeno (l’URL restituisce 404). L’elenco cambia da una versione all’altra: jQuery 1.10.2, la versione dello snippet del 2013, c’era fino a Chrome 153 ed è uscito con Chrome 154. Ogni elenco ha anche una data di scadenza, fissata a un anno dall’ultimo aggiornamento: superata quella, un Chrome non aggiornato torna alla cache partizionata per tutto.

Le condizioni per usare la cache condivisa

Dal codice di shared_resource_checker.cc, una richiesta finisce nella cache condivisa solo se:

  1. la destinazione è uno script, un foglio di stile o un dizionario di compressione;
  2. l’URL non ha query string, quindi jquery.min.js?ver=3.7.1 resta partizionato;
  3. la richiesta non ha flag che alterano la cache, come quelli di un ricaricamento forzato;
  4. sul sito che il visitatore sta navigando c’è stata di recente una navigazione della pagina principale o un’azione dell’utente;
  5. i cookie di terze parti sono consentiti per quella richiesta: in Incognito, dove Chrome li blocca di default, e per chi li ha disattivati vale la cache partizionata;
  6. l’URL corrisponde a uno dei pattern e l’elenco non è scaduto.

Firefox ha preso una posizione formalmente negativa (privacy, e browser che decidono quali risorse meritano la cache condivisa), Safari non si è espresso: su entrambi resta tutto partizionato. Sul guadagno reale è lo stesso Meenan a ridimensionarlo, nella discussione con Mozilla: “The actual performance gains from the cache sharing are very small. Not zero, but the kinds of numbers that you only see stat-sig differences at very large samples and scales.” In pratica: un effetto che si misura solo su campioni enormi.

Il test: stesso URL, due siti, Chrome 155

Per verificare il comportamento reale ho servito la stessa pagina da un server locale sotto due hostname diversi, sito-a.com e sito-b.com, risolti su 127.0.0.1 con il flag --host-resolver-rules. La pagina carica sei file jQuery da CDN pubbliche. Visito prima il sito A, poi il sito B nello stesso profilo, e leggo dal protocollo DevTools (evento Network.responseReceived, campo fromDiskCache) se il sito B riceve ogni file dalla cache o lo riscarica.

Risultati con Chrome 155.0.8059.80 stabile, Windows 11, 15 ottobre 2026:

RisorsaNell’elencoSito B
Google, 3.7.1 .min.jssìdalla cache
Google, 1.10.2 .min.jsnoriscaricato
Google, 3.7.1 .jsnoriscaricato
code.jquery.com, 3.7.1sìdalla cache
cdnjs, 3.7.1sìdalla cache
jsDelivr, 3.7.1noriscaricato

Il comportamento coincide con l’elenco: lo stesso jQuery 3.7.1 è condiviso se arriva da Google, code.jquery.com o cdnjs, partizionato se arriva da jsDelivr o in versione non minificata. Aggiungere integrity e crossorigin="anonymous" non cambia l’esito, anche quando solo uno dei due siti li usa. Lo stesso test con il Chromium 148 scaricato da Playwright, dove la funzione è disattivata di default, dà tutto partizionato.

Attenzione se replichi il test: il chromium-headless-shell che Playwright usa di default in modalità headless mostra come condivise tutte le risorse, anche quelle fuori elenco. Quel binario non riflette il comportamento di Chrome: usa channel="chrome" (il Chrome installato) o channel="chromium".

Lo script completo, eseguibile con pip install playwright e Chrome installato:

"""Quali librerie da CDN pubblica Chrome condivide tra siti diversi?

Due siti (sito-a.com, sito-b.com) puntano a un server locale e caricano gli
stessi URL. Se il sito B riceve la risorsa dalla cache su disco, la cache è
condivisa; se la riscarica, è partizionata per sito.
Requisiti: pip install playwright (usa il Chrome installato: channel="chrome").
"""
import functools, http.server, tempfile, threading
from pathlib import Path
from playwright.sync_api import sync_playwright

URLS = [
    "https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js",
    "https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js",
    "https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.js",
    "https://code.jquery.com/jquery-3.7.1.min.js",
    "https://cdnjs.cloudflare.com/ajax/libs/jquery/3.7.1/jquery.min.js",
    "https://cdn.jsdelivr.net/npm/jquery@3.7.1/dist/jquery.min.js",
]
PORT = 8765

# Pagina di test servita in locale: carica tutti gli URL come <script>
root = Path(tempfile.mkdtemp())
(root / "index.html").write_text(
    "<!doctype html><meta charset=utf-8>"
    + "".join(f'<script src="{u}"></script>' for u in URLS), encoding="utf-8")
class Silenzioso(http.server.SimpleHTTPRequestHandler):
    def log_message(self, *args):
        pass
handler = functools.partial(Silenzioso, directory=str(root))
server = http.server.ThreadingHTTPServer(("127.0.0.1", PORT), handler)
threading.Thread(target=server.serve_forever, daemon=True).start()

def visita(context, host):
    """Apre la pagina su un host e restituisce {url: servito_dalla_cache_su_disco}."""
    page = context.new_page()
    cdp = context.new_cdp_session(page)
    cdp.send("Network.enable")
    da_cache = {}
    cdp.on("Network.responseReceived", lambda e: da_cache.__setitem__(
        e["response"]["url"], e["response"].get("fromDiskCache", False)))
    page.goto(f"http://{host}:{PORT}/", wait_until="load")
    page.close()
    return da_cache

with sync_playwright() as pw:
    browser = pw.chromium.launch(channel="chrome", args=[
        # i due "siti" risolvono entrambi sul server locale
        "--host-resolver-rules=MAP sito-a.com 127.0.0.1, MAP sito-b.com 127.0.0.1"])
    context = browser.new_context()           # profilo nuovo: cache vuota
    visita(context, "sito-a.com")              # popola la cache dal sito A
    sito_b = visita(context, "sito-b.com")     # stesso URL, sito diverso
    print("Chrome", browser.version)
    for u in URLS:
        print("CONDIVISA   " if sito_b.get(u) else "PARTIZIONATA", u)
    browser.close()
server.shutdown()

Il costo dell’alternativa l’ho misurato nello stesso ambiente, da una connessione in fibra in Italia: 10 visite con profilo nuovo, quindi senza nulla in cache, verso ajax.googleapis.com in HTTP/2. DNS, TCP e TLS hanno richiesto una mediana di 104 ms (minimo 75, massimo 148), il download completo 196 ms. È il prezzo di ogni cache miss, di solito sul percorso critico del rendering, e su rete mobile cresce: nel test di Harry Roberts su 3G, tre origini esterne rendevano la pagina 1,765 secondi più lenta rispetto ai file in self-hosting.

Allora conviene? Il conto per un sito WordPress

Con la CDN di Google risparmi una richiesta e circa 30 KB compressi solo se si verificano tutte queste condizioni insieme:

  • il visitatore usa Chrome 150 o successivo, non Safari né Firefox;
  • ha i cookie di terze parti attivi e non naviga in Incognito;
  • il tuo URL coincide esattamente con uno dei pattern: host, versione e build minificata;
  • ha già in cache quel file, scaricato su un altro sito;
  • è la sua prima visita al tuo sito: dalla seconda, il jQuery servito dal tuo dominio è comunque in cache nella partizione del tuo sito.

In tutti gli altri casi paghi una connessione in più. E ci sono costi che non dipendono dalla cache:

  • Disponibilità. Se la CDN non risponde, jQuery non arriva e tutto ciò che ne dipende smette di funzionare. Il 27 aprile 2023 code.jquery.com ha avuto un’interruzione, segnalata in diretta da Harry Roberts.
  • Supply chain. A giugno 2024 il dominio cdn.polyfill.io, passato a un nuovo proprietario, ha iniettato codice malevolo in oltre 100.000 siti secondo le stime di Sansec. Con jQuery il rischio si riduce con SRI (sotto), che però protegge dalla manomissione del file, non dall’indisponibilità.
  • Privacy. Ogni richiesta verso ajax.googleapis.com comunica a Google l’indirizzo IP del visitatore. Per Google Fonts il Landgericht München I (sentenza del 20 gennaio 2022, 3 O 17493/20) ha ritenuto illecito questo trasferimento senza consenso, anche perché il font si poteva servire dal proprio server: lo stesso argomento vale per jQuery. È una sentenza di primo grado, non un precedente vincolante, ma il rischio si evita a costo zero.
  • Compatibilità con WordPress. L’handle jquery del core è un alias che carica jquery-core (3.7.1) e jquery-migrate (3.6.0). Sostituirlo con un URL esterno, come faceva lo snippet del 2013, scollega la libreria dalla versione testata con il core e può far sparire Migrate, che serve ai plugin con codice jQuery datato.

Per un sito WordPress jQuery va lasciato dove lo mette il core, servito dal tuo dominio e, se c’è, dalla tua CDN, con una cache lunga. Su questo blog /wp-includes/js/jquery/jquery.min.js?ver=3.7.1 esce con Cache-Control: public, max-age=31536000, immutable. Se vuoi migliorare davvero il caricamento, il lavoro utile è un altro: capire quali pagine hanno bisogno di jQuery e come vengono caricati gli script che ne dipendono, con async e defer.

Come verificare cosa carica il tuo sito

Dall’HTML della pagina, quale jQuery viene richiesto e da dove:

curl -s https://www.example.com/ | grep -oE '<script[^>]+src="[^"]*jquery[^"]*"'

Nella console di DevTools, la versione effettivamente in esecuzione e i file jQuery scaricati:

console.log('jQuery', window.jQuery ? jQuery.fn.jquery : 'non caricato');
performance.getEntriesByType('resource')
  .filter(e => /jquery/i.test(e.name))
  .forEach(e => console.log(e.name));

Su WordPress, dalla root dell’installazione, temi e plugin che caricano jQuery da CDN esterne:

grep -rnE "ajax\.googleapis\.com/ajax/libs/jquery|code\.jquery\.com|cdnjs\.cloudflare\.com/ajax/libs/jquery" wp-content/themes wp-content/plugins --include=*.php

Se trovi lo snippet del 2013 o una sua variante, rimuovilo: il core torna a caricare jquery-core e jquery-migrate dal tuo dominio senza altri interventi.

Se devi usare comunque una CDN pubblica

Fuori da WordPress, per una pagina statica senza CDN propria o per un prototipo, le regole minime sono quattro:

  1. https:// esplicito, mai l’URL senza protocollo;
  2. versione esatta e build minificata, le stesse che compaiono nell’elenco di Chrome, se vuoi la possibilità di un hit sulla cache condivisa;
  3. Subresource Integrity, con l’hash calcolato sul file che verrà servito;
  4. crossorigin="anonymous", necessario perché SRI funzioni su una risorsa di un’altra origine: Google la serve con Access-Control-Allow-Origin: *.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js"
        integrity="sha384-1H217gwSVyLSIfaLxHbE7dRb3v4mYCKbpQvzx0cegeju1MVsGrX5xXxAvs/HgeFs"
        crossorigin="anonymous"></script>

L’hash si calcola così:

curl -s https://ajax.googleapis.com/ajax/libs/jquery/3.7.1/jquery.min.js | openssl dgst -sha384 -binary | openssl base64 -A

Ho verificato lo snippet in Chrome 155: con l’hash corretto jQuery 3.7.1 si carica, con un carattere dell’hash alterato il browser blocca lo script. E, come visto nel test, SRI non impedisce l’uso della cache condivisa.

Per un progetto nuovo, prima ancora della CDN, la domanda è se jQuery serve: la versione 4.0.0 ha tolto il supporto a IE 10 e precedenti e diverse API deprecate, che oggi hanno equivalenti nativi in tutti i browser supportati.

Fonti

Articoli correlati

9 min lettura

Ottimizza il tempo di caricamento abbattendo la latenza dei round trip iniziali. Implementare correttamente i resource hint rel preconnect e link rel dns prefetch permette di anticipare la risoluzione DNS e l'handshake TLS, migliorando le performance percepite senza sprecare CPU.
22 mi piace
Lascia un commento 0
20 min lettura

Il Server Push HTTP/2 ottimizza le web performance inviando risorse al client prima della richiesta esplicita, eliminando i round trip. Appunti tecnici sul funzionamento del protocollo e configurazioni avanzate per Nginx e Apache destinate a developer e sistemisti.
22 mi piace
Lascia un commento 0

Autore

Commenti |3

Lascia un commento
  1. marni 3 commenti

    Salve , grazie per le informazioni , è un sistema che vorrei provare sul mio sito wp…..ho visto il codice da implementare, mi chiedevo tuttavia DOVE va inserito?
    grazie se puoi aiutarmi a capire..nel frattempo ho attivato il plugin che hai consigliato ma se riuscissi a farne a meno sostituendolo con lo script sarei più contenta :-)
    grazie per ogni aiuto
    cordiali saluti marni

  2. marni 3 commenti

    salve avevo lascito un commento ieri in attesa di moderazione ..non vedo più nulla…forse c’è stato un errore ? si è cancellato ..grazie se mi fa sapere qualcosa
    saluti Marni

    1. Giovanni Sacheli 775 risposte

      Buongiorno Marni, mi scusi se non le ho ancora risposto ma sono in ferie senza PC e siccome la risposta necessita un certo volume di parole preferivo aspettare di essere a casa. Provo comunque a dirle la mia – la stringa di codice va inserita nel tag HEAD dell’HTML. Dovrà cancellare il richiamo al file jQuery locale e sostituirlo con quello in hosting sui server di Google.
      In genere nei temi wordpress la sezione HEAD la trova nel file head.php.

      Spero di averla aiutata. Buon lavoro!

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Ultimi articoli aggiornati

7 min lettura

Una Content Delivery Network riduce la latenza distribuendo copie in cache delle risorse statiche attraverso PoP globali e nodi IXP. Analisi tecnica sul funzionamento dell'infrastruttura, dalla mitigazione DDoS all'ottimizzazione del routing per i principali provider CDN.
0 mi piace
Lascia un commento 0
9 min lettura

Ottimizza il tempo di caricamento abbattendo la latenza dei round trip iniziali. Implementare correttamente i resource hint rel preconnect e link rel dns prefetch permette di anticipare la risoluzione DNS e l'handshake TLS, migliorando le performance percepite senza sprecare CPU.
22 mi piace
Lascia un commento 0
10 min lettura

Scegliere il giusto framework WordPress è cruciale per la stabilità e le performance SEO nel lungo periodo. Analisi tecnica delle soluzioni premium come Genesis Framework per superare i limiti di codice e sicurezza tipici dei template commerciali obsoleti e non aggiornati.
0 mi piace
Lascia un commento 11

Richiedi un preventivo SEO e Google Ads

Porta il tuo sito web al livello successivo con l’esperienza di EVE Milano. La nostra agenzia di Search Marketing ha ricevuto dal 2010 oltre 1.400 richieste di preventivo, un segnale chiaro della fiducia che webmaster, imprenditori e manager ripongono nella nostra specializzazione tecnica e verticale nella SEO e PPC. Se la tua organizzazione cerca competenze specifiche per emergere nei risultati di Google e chatbot AI, noi siamo pronti a fornire quel valore aggiunto. Richiedi un preventivo ora e scopri la differenza tra noi e gli altri.
Richiedi un preventivo

Vuoi ricevere un avviso al mese con le nuove guide pubblicate?

Iscriviti alla newsletter!