JavaScript SEO: come Google indicizza SPA e siti React

La coda di rendering a due onde di Google, le trappole del CSR, la differenza fra SSR/SSG/ISR e i metodi pratici per verificare che il tuo sito JS sia indicizzato correttamente.

Come Google renderizza JavaScript: come funziona

Per una normale pagina HTML, Google fa un passo: scarica l'HTML e vede subito il contenuto. Per una pagina JavaScript, i passi sono tre: scaricare l'HTML → caricare il bundle JS → eseguirlo in Chromium headless per ottenere il DOM finale.

Google usa il proprio engine browser basato su Chromium — mantenuto aggiornato con aggiornamenti regolari. Quindi le funzionalità JavaScript moderne (ES2020+, import dinamico, WebAssembly) sono comprese da Googlebot. Il problema non è il supporto della sintassi, ma il ritardo del rendering e i vincoli di risorse durante la scansione.

Tre strategie di rendering JavaScript: SSR e SSG/ISR consegnano HTML pronto, mentre il CSR costruisce il DOM nel browser e finisce nella seconda ondata di indicizzazione.

Il rendering in numeri

3–4 s

Timeout di rendering

Se il JS non viene eseguito entro ~3–4 secondi, Google indicizza una pagina renderizzata parzialmente

≈ 150 ms

Tempo CSS

Le risorse che caricano in più di 150 ms possono bloccare il rendering e danneggiare l'efficienza della scansione

2 onde

Indicizzazione

I contenuti JS raggiungono l'indice con ritardo rispetto ai contenuti HTML — a volte giorni o settimane

100%

Google Evergreen

Googlebot si aggiorna all'ultimo Chromium stabile — le funzionalità JS moderne sono pienamente supportate

Indicizzazione a due onde: perché i contenuti JS arrivano dopo

Google divide la scansione in due fasi. La prima ondata è il crawling: Googlebot scarica l'HTML e tutti i link scoperti. La seconda ondata è il rendering: il bot mette in coda le pagine per il rendering, esegue il JS e indicizza il DOM finale.

Fra queste onde possono passare da ore a settimane — a seconda dell'autorevolezza del sito, del crawl budget e delle dimensioni della coda di rendering di Google. Se i tuoi testi chiave, gli H1, i meta tag o i link vengono generati solo via JS — arriveranno nell'indice con ritardo.

Conclusione pratica: Tutti i contenuti critici per la SEO — title, description, H1, link, dati strutturati — devono essere presenti nella risposta HTML del server. Così entrano nella prima ondata di indicizzazione senza ritardi.
L'indicizzazione a due onde è il motivo per cui le nuove pagine SPA «spariscono» dall'indice per 2–4 settimane dopo la pubblicazione, anche se l'HTML è fisicamente accessibile.

CSR — la trappola SEO principale

Il Client-Side Rendering (CSR) è quando il server invia un <div id="root"></div> vuoto e tutto l'HTML viene costruito nel browser dopo il caricamento del bundle JS. React, Vue e Angular in modalità SPA funzionano così di default.

Dal punto di vista SEO, il CSR è un problema per diversi motivi: 1) il rendering avviene solo nella seconda ondata; 2) le risorse bloccate al crawler (script di terze parti, CDN con robots.txt) non si caricano — la pagina si renderizza in modo incompleto; 3) qualsiasi errore JS = pagina vuota nell'indice.

html
<!-- CSR: what Google sees in the first wave -->
<html>
  <head>
    <title></title> <!-- empty -->
  </head>
  <body>
    <div id="root"></div> <!-- no content -->
    <script src="/bundle.js"></script>
  </body>
</html>
Errore critico: Generare <title>, <meta name="description"> e <h1> solo via JS lato client. Google riceve valori vuoti o di default durante il crawling iniziale e indicizza la pagina senza.

SSR, SSG e ISR: quale scegliere per la SEO

La soluzione ai problemi del CSR è la generazione HTML lato server. Esistono tre approcci principali, ciascuno ottimale per il proprio scenario.

Tre approcci di rendering JavaScript e il loro impatto SEO.

Confronto degli approcci di rendering

ApproccioHTML sul serverSEODati dinamici
CSRNoScadentePieno
SSRSì (a ogni richiesta)EccellenteAlto
SSGSì (alla build)EccellenteNo (serve rebuild)
ISRSì (a timer)EccellenteVia revalidazione
Approcci di rendering e casi d'uso adatti:
ApproccioIdeale per
CSRSPA autenticate, dashboard
SSRPagine personalizzate, cataloghi
SSGBlog, landing page, documentazione
ISRCataloghi prezzi, news

SSR (Server-Side Rendering) — l'HTML viene generato sul server a ogni richiesta. Massima freschezza dei dati, ma maggiore carico sul server. Next.js: generateMetadata e i componenti async nell'App Router forniscono SSR di default.

SSG (Static Site Generation) — l'HTML viene generato una sola volta alla build e servito come file statico. TTFB più veloce, ideale per la SEO. Limite: i cambi di dati richiedono una rebuild.

ISR (Incremental Static Regeneration) — un ibrido: le pagine vengono costruite in statico, ma dopo un periodo di revalidazione (revalidate: 3600) il server le rigenera in background alla richiesta successiva. In Next.js è l'approccio standard per la maggior parte delle pagine.

typescript
// Next.js App Router — SSG + ISR
export const revalidate = 3600; // revalidate every hour

export async function generateMetadata(): Promise<Metadata> {
  return {
    title: 'Product Catalogue',
    description: 'Full range with up-to-date prices',
  };
}

// All critical content is server-rendered:
export default async function CatalogPage() {
  const products = await fetchProducts(); // server fetch
  return (
    <main>
      <h1>Product Catalogue</h1>
      {products.map(p => <ProductCard key={p.id} {...p} />)}
    </main>
  );
}

Link dinamici e navigazione

Googlebot capisce solo i link in formato <a href="/url">. I link implementati via onClick, button, data-href o window.location sono invisibili al crawler. Questo significa che le pagine raggiungibili solo attraverso tale navigazione potrebbero non essere indicizzate.

  • Usa <a href> per tutti i link di navigazione — non button onClick con router.push()
  • Paginazione via parametri URL (?page=2) — Googlebot scansiona questi URL se compaiono nell'HTML
  • Router SPA: Next.js <Link> e React Router <Link> generano tag <a> corretti sul server
  • La navigazione con hash (#ancora) non crea URL separati nell'indice — non usarla per contenuti che devono posizionarsi in autonomia
  • Menu dinamici: se le voci di menu si renderizzano da un'API dopo il caricamento — Google potrebbe non scoprirle fino al rendering
Verifica: In Google Search Console → Controllo URL → «Visualizza come Googlebot» mostra quali link il crawler ha trovato sulla pagina. Se un link dinamico non compare nella lista — Googlebot non lo vede.

Lazy load e infinite scroll

Le immagini con loading="lazy" vengono caricate da Googlebot — il crawler moderno supporta il lazy loading. Ma c'è una condizione: l'immagine deve essere nel DOM con un attributo src corretto durante l'SSR. Se src viene impostato da JavaScript nel browser — il crawler vede un tag vuoto.

L'infinite scroll è un problema classico. Google potrebbe caricare il primo «schermo» di contenuti, ma le porzioni successive caricate via IntersectionObserver non sono garantite nell'indice. Soluzione: duplica i contenuti via paginazione con URL reali (/catalog?page=2) o aggiungi in HTML link alle pagine caricate in lazy.

PatternGooglebot lo vede?Raccomandazione
<img src="/img.webp" loading="lazy">SìUsalo
<img data-src="/img.webp"> (JS imposta src)NoEvita
Infinite scroll senza URLSolo il primo schermoAggiungi la paginazione
Contenuti dietro il pulsante «Mostra altro»NoMostra una parte in HTML
Contenuti a tab (tab nascoste)Sì, con ritardoDuplica in HTML

Diagnosi: come verificare il rendering JavaScript

La domanda diagnostica chiave è: «Cosa vede Googlebot sulla mia pagina?». Esistono diversi strumenti per rispondere.

Strumenti di diagnosi

  • Google Search Console → Controllo URL: mostra uno screenshot della pagina dopo il rendering di Googlebot, una lista di risorse e link scoperti. Il metodo di verifica più diretto.
  • «Visualizza come Googlebot» in GSC: esegue il vero crawler e restituisce l'HTML dopo il rendering — confrontalo con l'HTML sorgente.
  • curl o wget: ottieni l'HTML grezzo senza JS. Se i meta tag e i contenuti chiave sono nell'output di curl — saranno nella prima ondata di indicizzazione.
  • Disabilitare JS in Chrome DevTools (F12 → Ctrl+Shift+P → «Disable JavaScript»): modo rapido per vedere la pagina come farebbe il crawler senza rendering.
  • Screaming Frog con rendering: l'impostazione «Spider > Configuration > Rendering > Googlebot» permette di scansionare il sito col rendering JS — vedendo i link trovati post-render.
Esempio di configurazione Next.js per SSR/SSG:
bash
# Check what Google sees without JS rendering:
curl -A "Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)" \
  https://example.com/page

# Find all title / meta description / h1 in HTML response:
curl -s https://example.com/page | grep -E '<title>|<meta name="description"|<h1'

Checklist JavaScript SEO

Controlli chiave

  • Title e meta description presenti nella risposta HTML del server (SSR/SSG), non generati da JS
  • H1, H2 e il contenuto testuale principale accessibili senza JavaScript
  • Link di navigazione usano <a href>, non onClick / button
  • Dati strutturati (JSON-LD) in <head> nella risposta HTML
  • Immagini con src statico in HTML, loading="lazy" solo per immagini non-LCP
  • Robots.txt non blocca i file JS/CSS necessari al rendering
  • Nessun errore JS che rompa il rendering (controlla la Console di Chrome DevTools sulla pagina)
  • Core Web Vitals: LCP, INP, CLS rispettano le soglie di Google
  • Paginazione via parametri URL, non solo via stato JS
  • URL canonical (<link rel="[canonical](/glossary/canonical)">) in HTML, non impostati via JS

FAQ

Google riesce proprio a indicizzare JavaScript?
Sì. Googlebot usa Chromium Evergreen e può eseguire JS moderno. Il problema non è il supporto ma il ritardo: il rendering avviene nella seconda ondata, che può arrivare giorni o settimane dopo il crawl dell'HTML.
Un sito React/Next.js è automaticamente ben indicizzato?
Next.js con SSR/SSG — sì, se configurato correttamente. La React SPA pura (Create React App, Vite senza SSR) è CSR di default, il che è negativo per la SEO. La chiave è la generazione lato server di meta, H1 e contenuto principale.
Servono prerendering o rendering dinamico?
Il rendering dinamico (mostrare HTML diverso a Googlebot e agli utenti) è un approccio superato. Google lo permette ma non lo raccomanda. La strada giusta è SSR o SSG, che servono lo stesso HTML completo a tutti.
Posso usare React.lazy e code splitting?
Sì. Il code splitting via React.lazy e import dinamici migliora le performance e non danneggia la SEO, purché i contenuti critici (H1, testo principale) siano già nell'HTML iniziale. I componenti caricati a richiesta verranno comunque recuperati da Googlebot durante il rendering.
Come verifico che il mio sito sia indicizzato correttamente?
Usa Google Search Console → Controllo URL. Richiedi il rendering e confronta lo screenshot con ciò che vede un utente normale. Inoltre: verifica con curl la presenza di title, H1 e meta description nella risposta HTML senza JS.

Check it with SpiderHead

Free and local: run the audit on your own machine.

Try it