Checklist SEO per Next.js App Router: visibilità SSR, canonical, hreflang e crawler IA
Neaptide · 30 ottobre 2026 · 8 min di lettura
Verifica ciò che Next.js App Router restituisce davvero nell’HTML iniziale: titoli SSR visibili senza opacity:0, ereditarietà di alternates, link lingua e redirect.
In questo articolo

Un sito realizzato con Next.js App Router può apparire perfetto nel browser ma servire il titolo <h1> nascosto da style="opacity:0" nell’HTML iniziale, sovrascrivere i tag <link rel="alternate"> del layout padre o chiudere il selettore lingua dentro pulsanti <button> privi di href. Prima del rilascio è essenziale ispezionare la risposta HTTP grezza e l’HTML renderizzato dal server.
Anche con i Server Components, i wrapper di animazione client, generateMetadata() e middleware.ts determinano ciò che ricevono Googlebot e i crawler di ricerca IA (GPTBot, OAI-SearchBot, ClaudeBot, PerplexityBot). Di seguito presentiamo cinque problemi tecnici reali individuati e risolti sul sito multilingue Next.js 15 di Neaptide e una checklist ripetibile.
Cosa ha rilevato e corretto l’audit sul nostro sito Next.js 15?
Nell’ottobre 2026 abbiamo verificato l’HTML servito, gli header HTTP, i metadati e la sitemap nelle sei lingue di Neaptide. La tabella riassume le cinque anomalie tecniche riscontrate e il risultato verificato in produzione.
| Livello ispezionato | Difetto nell’HTML SSR iniziale | Correzione applicata e risultato verificato |
|---|---|---|
| Wrapper di animazione Reveal.tsx e Hero.tsx | Il titolo <h1>, l’introduzione e le schede uscivano con style="opacity:0;transform:translateY(12px)" fino all’idratazione (~1,2 s) | Rimosso opacity:0 dallo stato SSR iniziale; titoli e testi sono subito visibili nell’HTML grezzo |
| Helper generateMetadata() in lib/meta.ts | L’oggetto alternates: { canonical, languages } delle pagine sovrascriveva il layout rimuovendo <link rel="alternate" type="application/rss+xml"> | Aggiunto types: { "application/rss+xml": "https://www.neaptide.ai/feed.xml" } in pageMetadata() |
| Selettore lingua components/LangSwitcher.tsx | Le opzioni lingua venivano montate solo al clic come elementi <button> senza attributo href | Tutte le 6 lingue sono renderizzate nel DOM SSR come link <a href={...} hrefLang={l} lang={l}> |
| Redirect della radice (/) in middleware.ts | I bot che richiedevano / senza header geo o Accept-Language non ricevevano un header Vary completo | Redirect deterministico a x-default (/en) con Vary: Accept-Language, Cookie, x-vercel-ip-country, cf-ipcountry |
| Dati strutturati lib/schema.ts e app/sitemap.ts | Tre pagine di servizio impostavano areaServed: United States; la sitemap ometteva le immagini dei casi studio | Allineato areaServed e aggiunte copertine e grafici localizzati in sitemap.xml |
Queste osservazioni riguardano modifiche tecniche verificate sul nostro codice. Risolverle elimina ostacoli di rendering e scansione; il posizionamento organico e le citazioni nelle risposte IA dipendono comunque dalla concorrenza e dall’autorevolezza complessiva del sito.
Perché style="opacity:0" nelle animazioni penalizza LCP e crawler?
Molti siti Next.js racchiudono titoli e sezioni principali in componenti di animazione o Framer Motion con stato iniziale opacity: 0. Poiché Next.js pre-renderizza anche i Client Components sul server usando lo stato iniziale, l’HTML arriva con style="opacity:0" sul tag <h1> e sul testo sopra la piega.
Questo ritarda il Largest Contentful Paint (LCP) fino al completamento dell’idratazione JavaScript e presenta i titoli principali come nascosti ai crawler che leggono l’HTML grezzo. Mantieni il testo visibile nell’HTML SSR usando solo una leggera traslazione (transform: translateY(12px)) senza opacity: 0.
Come generateMetadata può cancellare i tag alternates del layout?
In Next.js App Router l’unione dei metadati tra layout e pagina è superficiale. Se una pagina restituisce un proprio oggetto alternates con canonical e languages, sostituisce interamente l’oggetto alternates del layout genitore.
Centralizza canonical, languages (incluso x-default) e types (come il feed RSS) in un’unica funzione condivisa:
export function pageMetadata(locale: Locale, opts: PageMetaInput): Metadata {
const canonicalUrl = `${SITE.url}${localeHref(locale, opts.path)}`;
const languages: Record<string, string> = {};
for (const l of LOCALES) {
languages[l] = `${SITE.url}${localeHref(l, opts.path)}`;
}
languages["x-default"] = `${SITE.url}${localeHref(X_DEFAULT_LOCALE, opts.path)}`;
return {
alternates: {
canonical: canonicalUrl,
languages,
types: {
"application/rss+xml": `${SITE.url}/feed.xml`,
},
},
};
}- Documentazione Next.js su generateMetadata
- Guida Google Search Central sulle versioni localizzate (hreflang)
Perché selettore lingua e redirect della radice devono essere espliciti per i bot?
Un selettore lingua che monta le opzioni solo al clic o usa pulsanti <button> non offre link HTML scansionabili tra le lingue. Renderizzare veri link <a href="/en/..." hrefLang="en" lang="en"> nel DOM dell’header permette ai crawler di scoprire direttamente tutte le varianti linguistiche.
Verifica inoltre il comportamento della radice / quando un bot effettua una richiesta senza cookie né header Accept-Language: il middleware deve reindirizzare in modo deterministico alla lingua x-default (/en) inviando l’header Vary appropriato.
Come verificare un rilascio Next.js App Router in sei passaggi
Esegui questi sei controlli prima di approvare il rilascio:
- Visibilità SSR grezza: scarica la pagina con curl -s e verifica che l’unico <h1> e il testo principale compaiano senza style="opacity:0".
- Parità di canonical e hreflang: controlla che ogni URL localizzato esponga il canonical autoreferenziale, le 6 lingue, x-default e il link RSS.
- Link HTML scansionabili: conferma che menu, footer e selettore lingua usino tag <a href="..."> nell’HTML iniziale.
- Redirect della radice: testa / con curl -I e un header Accept-Language vuoto per confermare un singolo redirect permanente verso la destinazione prevista.
- Coerenza JSON-LD: verifica che WebPage, Service, Article, BreadcrumbList e FAQPage corrispondano al testo visibile e alla lingua della pagina.
- Copertura sitemap.xml e llms.txt: controlla che tutte le rotte e le immagini principali siano presenti in /sitemap.xml e /llms.txt.
curl -s https://www.neaptide.ai/en | grep -E '<h1|rel="canonical"|hrefLang|application/rss\+xml'
curl -I -H 'Accept-Language:' https://www.neaptide.ai/Se desideri una verifica tecnica del tuo sito Next.js, Neaptide esegue audit SEO e GEO con controlli HTTP riproducibili e ticket pronti per gli sviluppatori.