Veloce di default: performance e SEO che sopravvivono a un crawler

Scritto da

Rendering statico, immagini oneste e una SEO che funziona anche con JavaScript disattivato. Come questo sito resta veloce e trovabile, e come lo verifico.

Pubblicato il

Veloce di default: performance e SEO che sopravvivono a un crawler

Aggiornato

Nota d'archivio. Questo articolo è stato pubblicato per la prima volta nella data indicata sopra. Descrive gli strumenti e il metodo usati allora. Il sito attuale di Jumpino Tech usa Astro e Rust. Prezzi, versioni e disponibilità possono cambiare: verifica la documentazione prima di implementare.

Velocità e posizionamento non sono funzioni da aggiungere alla fine. Sono decisioni di architettura che prendi il primo giorno. Ecco come questo sito resta veloce e trovabile, e come verifico entrambe le cose invece di andare a intuito.

Renderizzalo una volta, servilo per sempre

La risposta più veloce è un file statico. Ogni pagina qui è pre-renderizzata in fase di build, un documento HTML per rotta e per lingua. Nessun lavoro del server a ogni richiesta, nessun viaggio al database, nessun fetch lato client prima del primo paint. Una CDN la serve dal bordo della rete e il browser inizia subito a disegnare.

È anche il motivo per cui il sito funziona con JavaScript disattivato. Il contenuto vive nell’HTML, non viene assemblato nel browser, quindi crawler, screen reader e connessioni lente ricevono la pagina intera. Le animazioni sono uno strato in più, mai un requisito.

export function generateStaticParams() {
  return routing.locales.map((locale) => ({locale}));
}

export default async function Page({params}) {
  const {locale} = await params;
  setRequestLocale(locale); // enables fully static rendering
  return <Sections />;
}

Le immagini sono dove muore la velocità

L’errore di performance più comune è spedire immagini pesanti. I miei diagrammi di progetto erano nati come file PNG da 1,7 MB, quasi 12 MB in totale. Convertirli in WebP li ha portati a 0,76 MB senza differenze visibili. Poi ogni immagine passa per next/image, che consegna al browser AVIF o WebP alla dimensione esatta richiesta dal layout.

  • AVIF o WebP in automatico, mai il file originale pesante.
  • Un attributo sizes esplicito, così c’è una variante giusta per ogni breakpoint.
  • Caricamento lazy sotto la piega e priority solo sull’hero, per un LCP rapido.
  • Dimensioni riservate, così il layout non si sposta mentre arrivano le immagini.
import Image from "next/image";

// Below the fold: lazy, sized, served as AVIF/WebP at the right width.
<Image
  src="/renders/rn-04.webp"
  alt="Polysong, system diagram"
  fill
  sizes="(max-width: 640px) 100vw, 50vw"
  className="object-cover"
/>
Schema di HTML statico e immagini ridimensionate che producono un caricamento veloce
HTML statico più WebP della dimensione giusta: la pagina è veloce prima ancora di eseguire uno script.

La SEO è idraulica, non magia

La ricerca è soprattutto igiene fatta con costanza. Ogni pagina dichiara il proprio URL canonico e gli alternate hreflang per tutte e quattro le lingue, così Google indicizza la pagina giusta per ogni lingua invece di fonderle. Una sitemap elenca ogni rotta in ogni lingua, i dati strutturati descrivono chi sono e dove lavoro, e ogni pagina porta titolo, descrizione e immagine Open Graph localizzati.

Il bug da citare: un singolo canonico ereditato dal layout faceva puntare ogni articolo del blog alla home. Un canonico condiviso è peggio di nessun canonico, perché dice attivamente a Google di scartare la pagina.

export async function generateMetadata({params}) {
  const {locale, slug} = await params;
  const path = "/blog/" + slug;
  return {
    title: post.title,
    description: post.excerpt,
    alternates: {
      canonical: localeUrl(locale, path),
      languages: {
        en: localeUrl("en", path),
        it: localeUrl("it", path),
        de: localeUrl("de", path),
        ro: localeUrl("ro", path)
      }
    },
    openGraph: { type: "article", images: [path + "/01.webp"] }
  };
}

Dai all’agente una checklist che ricorda

Quando un agente AI lavora su questo codice, non dovrebbe riscoprire le regole di performance e SEO a ogni sessione. Le tengo in un file di memoria markdown che legge per primo, così le convenzioni persistono tra le sessioni e l’agente agisce su di esse invece di tirare a indovinare.

# PERFORMANCE.md  (read me before touching the UI)

## Images
- Convert source art to WebP before commit, target under 200 KB.
- Always render through next/image with an explicit sizes attribute.
- Use priority only on the LCP image (the hero); everything else stays lazy.

## Rendering
- Pages must stay static: generateStaticParams + setRequestLocale.
- No client-side data fetching above the fold.

## SEO
- Every page sets its own canonical and hreflang for en, it, de, ro.
- Update the sitemap and JSON-LD whenever routes change.
Un agente AI che legge una checklist di performance in markdown prima di modificare il sito
Un file di memoria markdown mantiene le regole di performance e SEO persistenti tra le sessioni dell’agente.

Misura, non andare a sensazione. Non spedire niente basandoti su una sensazione. Builda, elenca le dimensioni di bundle e asset, lancia Lighthouse e sistema prima il numero più grande. Quei 12 MB di immagini erano invisibili finché non ho ordinato i file per dimensione.

Veloce e trovabile si rivelano la stessa disciplina: fai il lavoro noioso a livello di architettura, tieni il payload piccolo e lascia che la piattaforma serva file statici. La parte notevole è che non c’è niente di notevole.

Parliamone

Parliamone

Raccontami cosa hai in mente. Rispondo di solito entro un giorno lavorativo.