Perché "come scrivere JSON-LD corretto" non è la domanda giusta
Chi cerca "come scrivere JSON-LD" di solito arriva a una documentazione di proprietà, a un esempio copia-incollato, e a un validatore che restituisce esito positivo. Il problema è che il semaforo verde certifica la grammatica, non il significato. E il significato è esattamente ciò che Google e i motori AI leggono.
La differenza fondamentale è quindi cosa ci dice la Sintassi (il JSON è valido), la Semantica (il router collega le pagine) e l'Ontologia (il Knowledge Graph capisce cosa vendiamo).
Le relazioni dichiarate in Schema.org sono asserzioni su come è fatto il sito. Quando
sono sbagliate — quando un hasPart dichiara un contenimento che non esiste, quando un
breadcrumb vive dentro un Article invece che nella WebPage, quando un
@id punta a un nodo che non è mai stato definito — nessuno strumento automatico di
validazione sintattica lo segnala. Eppure il grafo che le macchine costruiscono contiene quegli errori.
→ Validatori automatici
→ Audit umano
→ Effetti sul Knowledge Graph
Da dove viene questo metodo
La metodologia dell'audit semantico non nasce da un'ipotesi teorica. Si è formata per accumulo: un errore alla volta, un template che generava lo stesso problema su centinaia di pagine, una correzione che rivelava il pattern successivo. Non c'è stata una fase iniziale in cui qualcuno ha "progettato" questa architettura: c'è stato un percorso di scoperta, e questo documento è la formalizzazione di quello che ha funzionato.
Da questa esperienza è derivata una convinzione precisa: i validatori controllano la grammatica,
non il significato. Un hasPart verso una pagina esterna passa la validazione; un
breadcrumb dentro un Article passa la validazione; un @id che punta a un nodo
inesistente passa la validazione. Il semaforo verde dice "sintassi OK", non "ontologia coerente".
Quando il numero di pagine supera la capacità di controllarle una per una, gli errori non spariscono — cambiano forma, diventando pattern sistemici invece di refusi isolati. L'architettura, non la singola pagina, è ciò che va progettato con cura.
Cosa analizza un Audit Schema.org
L'audit non si limita a controllare se il codice è valido. Analizza il grafo nella sua interezza e verifica che regga dal punto di vista logico. Ecco le aree che controlla.
1. Coerenza dei tipi
Ogni @type è dichiarato una sola volta nel grafo, senza contraddizioni? Lo stesso @id non viene classificato in un punto come WebPage e in un altro come WebApplication? Questa è la Chimera Ontologica: il validatore non la segnala, ma l'AI che legge il grafo si blocca davanti a due tipi incompatibili sullo stesso nodo.
2. Risolvibilità degli @id
Ogni @id referenziato esiste davvero da qualche parte — nello stesso @graph o in una pagina raggiungibile? Questa analisi scova i Dangling Node: archi che puntano al nulla. Su una pagina è un dettaglio; su 250 pagine è un pattern che indebolisce l'intero grafo.
3. Appropriatezza delle proprietà
Il contenimento è dichiarato con hasPart solo quando è reale? I collegamenti tematici verso l'esterno usano significantLink? Il breadcrumb è nella WebPage o si è infiltrato dentro l'Article? Sono tutte scelte che passano il validatore ma cambiano il significato del grafo.
4. Parità dei dati
Ogni valore nel JSON-LD (rating, prezzo, claim) compare anche nel DOM visibile all'utente? Il markup nascosto è una violazione delle linee guida Google e può portare a un'azione manuale per markup ingannevole.
5. Separazione contenitore / contenuto
I loop di navigazione (menu, correlati, hub) puntano solo ai contenitori (#webpage) o anche ai contenuti finali (#article, #app)? Un loop che forza il tipo WebPage su un ID di software è un errore invisibile al validatore ma letale per il grafo.
6. Convenzione di naming dei frammenti
Ogni pagina rispetta una convenzione rigida per i frammenti (#webpage, #article, #breadcrumb, #organization)? Un audit efficace controlla che gli @id siano coerenti tra le pagine del sito, senza sorprese generate da template diversi.
7. Diagnostica dei conflitti di risoluzione
Quando un errore sembra non avere senso — perché due pagine strutturalmente identiche producono esiti diversi — la causa è nella risoluzione dei riferimenti, non nella pagina. Lo stesso @id che appare in più proprietà con range diversi, un array con elementi misti (tipizzati e non tipizzati), un frammento custom non riconosciuto: sono tutti meccanismi che generano errori downstream, apparentemente scorrelati dalla causa reale. Il capitolo Diagnostica copre il metodo per isolarli.
Le regole operative: come scrivere JSON-LD che regge il grafo
Diciannove pattern che ricorrono in produzione, con esempi di codice corretto e sbagliato. Ogni regola esiste perché un caso reale l'ha generata: un template che sbagliava su 250 pagine, un audit che ha rivelato un Dangling Node, un validatore che dava semaforo verde su una relazione ontologicamente rotta.
1. Contenimento vs collegamento: hasPart vs significantLink
hasPart indica composizione letterale: X contiene fisicamente Y.
significantLink indica rilevanza editoriale: Y è uno dei link più importanti della
pagina, ma non è contenuto in X.
- Usa
hasPart: CollectionPage → sue pagine figlie, Libro → Capitolo, Corso → Lezione, Article → FAQPage. - Usa
significantLink: WebPage → pagine esterne correlate, Articolo → fonti citate, Hub → sibling tematici.
Test operativo: traduci la relazione in italiano ("X contiene fisicamente Y").
Se suona forzata, la proprietà corretta è significantLink.
2. La trappola sintattica di significantLink
significantLink accetta esclusivamente stringhe. Non Reference Node, non oggetti.
Se passi {"@id": "..."} il validatore Schema.org dà errore bloccante.
// ❌ Errore validatore
"significantLink": [{ "@id": "https://sito.it/pagina/#article" }]
// ✅ Corretto
"significantLink": ["https://sito.it/pagina/#article"]
Critico: includi sempre il frammento finale (#article,
#app, #webpage) nella stringa. La sintassi resta valida e l'AI sa
esattamente a quale nodo stai puntando, evitando i Dangling Node.
3. Il breadcrumb: dove va davvero
Il breadcrumb appartiene alla WebPage, non all'Article. Il motivo è
ontologico: il breadcrumb descrive un percorso di URL, e gli URL appartengono alle pagine, non
ai contenuti. Molti siti lo mettono dentro l'Article e Google mostra comunque il percorso in
SERP — da qui la falsa percezione che vada bene. Su scala diventa un pattern sistemico.
// ✅ Pattern corretto
{
"@type": "WebPage",
"@id": "/pagina/#webpage",
"breadcrumb": { "@id": "/pagina/#breadcrumb" },
"mainEntity": { "@id": "/pagina/#article" }
}
Regola architetturale: assegna il breadcrumb a livello di template di pagina,
non di entità. In WordPress → single.php; in React → layout di pagina, non
componente di contenuto.
4. Reference Node vs Stub Node
Quando referenzi un nodo definito altrove, usa solo @id. Aggiungere
@type senza le proprietà essenziali (name, publisher,
ecc.) crea un'entità parziale, che su larga scala genera ambiguità su quale sia la versione
autorevole dell'entità.
// ❌ STUB — crea entità parziale
"hasPart": [
{ "@type": "WebPage", "@id": "/p1/#webpage", "url": "/p1/" }
]
// ✅ REFERENCE NODE — grafo appiattito
"hasPart": [
{ "@id": "https://sito.it/p1/#webpage" }
]
5. ItemList: quando generarlo e quando no
ItemList è un tipo, non una proprietà. Va generato solo quando la pagina è
strutturalmente una lista: hub, indici di categoria, classifiche.
- Sì: pagine hub, categorie, ranking.
- No: articoli singoli, prodotti, tool. E mai per le sezioni FAQ — quelle usano
FAQPagecon struttura domanda/risposta.
Ogni ListItem deve avere un name leggibile. Un URL nudo è segnale povero.
6. Formati dati misti: gestirli entrambi
Una proprietà può essere popolata in due forme valide. Il generatore deve riconoscerle entrambe.
// Formato A
["/pagina-a/", "/pagina-b/"]
// Formato B
[{ "id": "/pagina-a/", "type": "..." }]
Se il codice si aspetta un solo formato, funziona finché qualcuno — magari mesi dopo, magari un collaboratore diverso — scrive la lista nell'altro. Il markup smette silenziosamente di essere valido.
7. Conflitti di ID: il ponte sameAs
Se inietti un'Organization globale site-wide e una Organization
ricca sulla pagina "Chi Siamo" con @id diverso, Google rischia di trattarle come
due entità distinte. Non modificare il template globale: collega le due entità con
sameAs.
{
"@type": "Organization",
"@id": "https://sito.it/#org-detail",
"sameAs": "https://sito.it/#org"
}
8. La Chimera Ontologica
Un ID dichiarato come WebApplication in un punto del grafo e come
WebPage in un altro è una contraddizione logica. Il validatore tace, l'AI si
blocca davanti a due tipi incompatibili sullo stesso nodo. La causa più frequente: un loop di
navigazione che forza @type: "WebPage" su ogni elemento, anche quando l'ID
appartiene altrove a un software.
Regola: i loop di navigazione (menu, correlati, hub) devono puntare solo a
contenitori (#webpage), mai a contenuti finali (#article,
#app).
9. Target typing e rich snippet
@type: "WebApplication" è semanticamente corretto ma Google richiede la classe
SoftwareApplication più operatingSystem per il rich result. Il tipo
singolo non basta. Usa l'ereditarietà multipla.
"@type": ["SoftwareApplication", "WebApplication"],
"operatingSystem": "Web Browser"
Stesso pattern quando un vincolo di range blocca un link semanticamente valido:
// teaches si aspetta Text o DefinedTerm
"@type": ["WebApplication", "DefinedTerm"],
"@id": "/software/#app",
"name": "Protocollo SEO Avanzato"
10. Contenitore ≠ contenuto
Una pagina che ospita un tool non è il tool. Separa le entità con mainEntity.
// ❌
"@type": ["WebPage", "WebApplication"]
// ✅
{
"@type": "WebPage",
"@id": "/tool/#webpage",
"mainEntity": {
"@type": "WebApplication",
"@id": "/tool/#app",
"name": "Calcolatore SEO"
}
}
Anti-pattern: applicare WebPage o Article
indiscriminatamente in un header condiviso. Correggi con routing semantico — il tipo dipende
dai dati, non solo dal layout.
11. about vs mentions
about→ tema principale (1–2 entità max).mentions→ riferimenti secondari.
Troppi about diluiscono il segnale: la pagina compete per query sbagliate.
12. Parità dei dati
Ogni valore nel JSON-LD deve essere visibile nel DOM renderizzato. Se dichiari
aggregateRating: 4.8 e nel DOM non c'è un badge con "4.8", stai violando le linee
guida Google. Rischio: azione manuale per markup ingannevole.
- Se dichiari
price: 0, la pagina deve dire "Gratis" o "0€". - Se dichiari
ratingValue: 4.8, deve esserci un badge visibile con "4.8". - Le icone puramente grafiche vanno marcate
aria-hidden="true".
13. Convenzione di naming dei frammenti
Adotta una convenzione rigida e verificala periodicamente. Esempio:
#webpage→ contenitore pagina#article→ entità articolo#app→ entità applicazione#breadcrumb→ entità breadcrumb#organization→ organizzazione
I validatori non aprono gli URL: non verificano che l'@id esista davvero
dall'altra parte. Un Fragment Audit — estrarre tutti gli @id usati nei loop di
navigazione e verificare che puntino ai contenitori — è l'unico modo per scovarli.
14. hasPart + ItemList: la trappola dell'indice
Non inserire un ItemList dentro hasPart. ItemList è
classificato come Intangible (struttura dati astratta), non come
CreativeWork. Un libro ha capitoli, non "una lista di dati".
// ❌
"hasPart": { "@type": "ItemList", "itemListElement": [...] }
// ✅
"hasPart": [
{ "@id": "https://sito.it/capitolo-1/#webpage" },
{ "@id": "https://sito.it/capitolo-2/#webpage" }
]
15. Il pattern bidirezionale CollectionPage
Su un vero Topic Cluster — Pillar Page che organizza una serie di approfondimenti — la bidirezionalità è preziosa:
- La CollectionPage (padre) dichiara
hasPartverso i figli. - Ogni pagina figlia dichiara
isPartOfverso il padre. - Entrambi con Reference Node puri (
@idassoluto, niente@type).
16. Global identifiers: citare terze parti
Se citi un software terzo (isBasedOn: SoftwareApplication), Google attiva i
filtri rich-snippet commerciali e ti inonda di warning per offers e
operatingSystem mancanti. La soluzione pigra è declassare a Thing —
che lobotomizza il grafo. La soluzione corretta è puntare a un IRI globale.
"isBasedOn": {
"@type": "SoftwareApplication",
"@id": "https://www.wikidata.org/wiki/Q161676",
"name": "WordPress"
}
Il bot riconosce una citazione esterna, non una distribuzione locale: i filtri commerciali si
disinnescano, il @type resta preservato.
17. Conflitto @id cross-proprietà
Quando lo stesso @id appare in più proprietà con range diversi
all'interno dello stesso grafo, e non è definito localmente con un @type
esplicito, il validatore lo risolve una sola volta — alla prima occorrenza — e
propaga il tipo risultante a tutte le altre. Se il tipo risolto è debole (Thing),
il declassamento può invalidare proprietà che richiedono range più restrittivi come
CreativeWork.
// ❌ Conflitto: stesso @id in hasPart (richiede CreativeWork)
// e in isRelatedTo (range libero), senza @type locale
{
"@graph": [
{
"@type": "CollectionPage",
"hasPart": [
{ "@id": "https://sito.it/pagina/#article" }
]
},
{
"@type": "Service",
"isRelatedTo": [
{ "@id": "https://sito.it/pagina/#article" }
]
}
]
}
// ✅ Soluzione: differenziare i frammenti
// hasPart punta al contenuto (#article)
// isRelatedTo punta al contenitore (#webpage)
{
"@graph": [
{
"@type": "CollectionPage",
"hasPart": [
{ "@id": "https://sito.it/pagina/#article" }
]
},
{
"@type": "Service",
"isRelatedTo": [
{ "@id": "https://sito.it/pagina/#webpage" }
]
}
]
}
Il segnale diagnostico non è il messaggio d'errore, che appare downstream in
un punto apparentemente non correlato. È la presenza dello stesso @id in
più proprietà. Quando trovi quel pattern, cerca lì la causa prima di guardare
altrove.
18. Auto-inferenza da frammento: la dipendenza fragile
I Reference Node puri ({"@id": "..."}) funzionano solo se il
validatore riesce a risolvere l'@id. Esistono tre livelli di risoluzione, in
ordine di priorità:
- Definizione locale: l'
@idesiste nel@graphdella pagina corrente come oggetto con@typeesplicito. Risoluzione garantita. - Auto-inferenza dal frammento: se il frammento è in whitelist di convenzioni note (
#webpage→WebPage,#article→Article,#faq→FAQPage,#breadcrumb→BreadcrumbList), il validatore inferisce il tipo. - Declassamento a
Thing: se il frammento è custom e non c'è definizione locale, il tipo èThing. Questo può invalidare proprietà con range più restrittivi.
Conseguenza pratica: i Reference Node puri funzionano "per fortuna" quando
usano frammenti standard. Basta un frammento custom in un punto del grafo e il meccanismo si
rompe silenziosamente. Un audit efficace verifica che ogni @id referenziato sia
in whitelist o definito esplicitamente nel grafo locale.
19. Indice come vista, non contenitore
Una pagina che elenca altre pagine non le contiene. Sono due relazioni ontologicamente diverse:
- Contenimento (
hasPart): il contenitore è fatto fisicamente delle parti. Un libro contiene capitoli. - Elencazione (
significantLink): la pagina rimanda a risorse esterne, senza contenerle. Un indice elenca i capitoli di un libro.
La tentazione di usare hasPart su un indice è forte ("l'indice contiene i
capitoli"). È ontologicamente sbagliato: i capitoli sono parte del libro,
non dell'indice che li elenca.
// ❌ Indice con hasPart verso i capitoli
{
"@type": "CollectionPage",
"@id": "/indice/#webpage",
"hasPart": [
{ "@id": "/capitolo-1/#webpage" },
{ "@id": "/capitolo-2/#webpage" }
]
}
// ✅ Indice con significantLink (stringhe) verso i capitoli
{
"@type": "CollectionPage",
"@id": "/indice/#webpage",
"significantLink": [
"https://sito.it/capitolo-1/#webpage",
"https://sito.it/capitolo-2/#webpage"
]
}
Il contenimento resta sul contenitore reale (il libro, l'opera, la raccolta). L'indice è una vista che espone l'elenco, non un contenitore fisico.
Conseguenza sui pattern narrativi: in un contenuto a bivi (percorso
interattivo, tutorial a step, albero di scelte), ogni nodo appartiene al nodo precedente con
isPartOf, e i nodi successivi si dichiarano con significantLink.
L'indice resta la vista d'insieme con significantLink verso tutti i nodi. Nessun
hasPart.
Diagnostica: quando due strutture identiche danno esiti diversi
C'è un momento, in ogni audit, in cui due pagine apparentemente identiche ottengono risultati opposti dal validatore. Una passa, l'altra no. Il codice sembra lo stesso. La reazione istintiva è pensare a un errore del validatore, a una cache, a un problema temporaneo.
Non è niente di tutto questo. Il motivo è che i validatori risolvono i riferimenti in modo contestuale, non isolato: dipende da cosa c'è nel resto del grafo, non solo da quello che scrivi nel punto specifico.
Il principio: risoluzione una volta sola, propagazione
Quando un validatore incontra un riferimento a un @id, lo risolve
una sola volta — alla prima occorrenza — e usa il risultato per tutte le
occorrenze successive. La decisione non viene rivalutata per ogni proprietà.
Se la prima occorrenza è in un contesto che non permette di determinare il tipo (perché
l'@id non è definito localmente e il frammento non è riconoscibile), il
validatore lo classifica come Thing. Quel Thing viene poi
propagato anche alle altre proprietà che referenziano lo stesso @id.
Se una di quelle proprietà richiede un range più specifico (hasPart richiede
CreativeWork, per esempio), il Thing non è compatibile e il
validatore segnala l'errore sulla proprietà sbagliata — apparentemente
scorrelata dalla causa reale.
Il pattern sospetto: stesso @id in più proprietà
Il segnale diagnostico più importante non è il messaggio d'errore, è la presenza
dello stesso @id in più di una proprietà all'interno dello stesso
grafo.
Quando trovi questo pattern, controlla due cose:
- L'
@idè definito localmente con un@typeesplicito? Se sì, nessun problema: la risoluzione è garantita e non c'è declassamento. - Le proprietà che lo usano hanno range compatibili? Se una è
hasPart(range ristretto) e l'altra è una proprietà generica, il range più restrittivo richiede un tipo più forte delThing.
Se l'@id non è definito localmente e le proprietà hanno range incompatibili, hai
trovato la causa. Differenzia i frammenti: una proprietà punta al contenuto
(#article), l'altra al contenitore (#webpage). Sono due entità
diverse per natura, e il conflitto si risolve senza toccare il tipo dell'articolo.
Incoerenza interna agli array
Un caso correlato e subdolo: un array (hasPart, mentions,
isRelatedTo) in cui alcuni elementi hanno @type esplicito
e altri no. Il validatore cerca coerenza interna, e in assenza di essa:
- Può declassare gli elementi tipizzati al tipo più debole degli altri
- Può segnalare errori sugli elementi non tipizzati anche se il problema è l'uniformità
- Il messaggio d'errore può non nominare l'elemento effettivamente problematico
Regola operativa: in un array, gli elementi devono essere tutti
tipizzati o tutti senza @type. Mai misti. Un array
misto è un array che nasconde problemi.
Padre URL vs padre semantico
Due concetti diversi che vengono spesso confusi, e che i validatori non distinguono.
Il padre URL è fisico: si ottiene tagliando l'ultimo segmento dell'URL. Ogni rotta ne ha uno, che esista o no come pagina pubblica.
Il padre semantico è quello dichiarato in isPartOf. Non sempre
coincide con il padre URL, e non deve.
Esempi tipici di scostamento intenzionale:
- Una pagina di servizio può essere figlia strutturale di un minihub e correlata tematicamente (via
about) a una pagina enciclopedica - Un volume può appartenere alla sua serie, non alla pagina indice dei volumi
- Un tool può essere figlio di un centro strumenti invece che della sua categoria URL
- Una pagina di primo livello può riferirsi alla home, anche se il suo padre URL "naturale" sarebbe un prefisso che non è una pagina pubblica
Il test diagnostico: confronta URL padre fisico e isPartOf
dichiarato. Gli scostamenti non sono automaticamente errori. Sono scelte. Ma vanno
verificate una per una e classificate in due categorie: scelte deliberate e
errori probabili.
Metodo di verifica in quattro passi
Estrai il JSON-LD della pagina problematica. Individua tutte le entità con @type esplicito e tutti i riferimenti @id senza definizione locale.
Costruisci una mappa @id → [proprietà che lo usano]. Identifica gli @id che appaiono in più di una proprietà.
Per ogni @id in conflitto: è definito localmente con @type? Se no, il frammento è in whitelist di convenzioni note? Se nemmeno, differenzia i frammenti tra le proprietà, o aggiungi una definizione esplicita.
Per ogni @id referenziato ma non definito localmente, verifica che il frammento sia in whitelist. Se non lo è, aggiungi definizione esplicita o cambia frammento.
Caso pratico (generico)
Due pagine con struttura praticamente identica:
- Entrambe figlie della stessa pagina madre
- Entrambe referenziate in
hasPartdella madre - Entrambe con entità propria di tipo
TechArticle - Entrambe senza
@typenel riferimento
Una passa il validatore, l'altra no. Nessuna differenza apparente nel codice delle due pagine.
Diagnosi: la pagina che fallisce è referenziata anche da un'altra
proprietà — per esempio isRelatedTo di un Service — con lo
stesso @id. Il validatore ha risolto quel @id alla prima occorrenza
(in isRelatedTo) e l'ha classificato come Thing perché non definito
localmente, poi ha propagato il Thing a tutte le altre occorrenze — inclusa
quella in hasPart, che richiede un range CreativeWork e rifiuta
Thing.
Soluzione: differenziare i due riferimenti.
hasPart verso l'articolo (#article),
isRelatedTo verso la pagina (#webpage). Le due entità sono
diverse per natura — contenuto vs contenitore — e il conflitto si risolve
senza toccare il tipo dell'articolo.
Lezione: quando un riferimento è ambiguo, il validatore non segnala
l'ambiguità: segnala l'errore downstream, in un punto che può sembrare non
correlato. Il segnale utile è la presenza dello stesso @id in più proprietà. È
quel pattern che va cercato per primo quando un errore "non ha senso".
Mappa sintomo → regola
Per chi arriva con un problema già identificato, questa tabella fa da indice rapido.
| Sintomo | Regola |
|---|---|
| Validatore verde, rich snippet assente | §9 Target typing |
| "Elemento senza nome" su entità annidata | §4 Reference Node |
| Breadcrumb in SERP ma grafo incoerente | §3 WebPage |
Dangling node, @id orfano | §1, §13 |
| Due organizations nel Knowledge Graph | §7 sameAs |
| FAQ trattate come lista link | §5 scope |
| Tipo sbagliato forzato da loop | §8 Chimera |
| JSON-LD enorme che rallenta la pagina | §4 grafo appiattito |
ItemList su prodotto singolo | §5 scope |
Errore validatore su significantLink | §2 solo stringa |
| Due pagine identiche con esiti diversi dal validatore | §17, Diagnostica |
| Errore su proprietà apparentemente scorrelata | Diagnostica, Passo 2 |
Errore Thing su una proprietà con range ristretto | §17 |
Indice con hasPart verso le sue voci | §19 |
| Reference Node che funziona su una pagina e non su un'altra | §18 |
In caso di dubbio
Traduci la tripla JSON-LD in una frase semplice (Soggetto → Verbo → Oggetto). Se la frase suona forzata, la proprietà è sbagliata — indipendentemente da cosa dice il validatore.
"Questo Article contiene fisicamente un Breadcrumb" → forzato → il breadcrumb appartiene alla WebPage.
"Questa WebPage contiene quest'altra WebPage esterna" → forzato → usa significantLink.
"Questa WebPage è una WebApplication" → sbagliato → usa mainEntity.
"Questo indice contiene fisicamente i suoi capitoli" → forzato → l'indice elenca, il libro contiene.
Regola finale: ogni regola sopra esiste perché un validatore ha detto "OK" mentre il Knowledge Graph diceva "no". Fidati del grafo, non della spunta verde.
La checklist operativa
Domande concrete che un Audit Semantico pone a ogni pagina. Non è un test da spuntare una volta: è una griglia da riapplicare quando si aggiunge un tipo di contenuto o si automatizza una regola.
Sul singolo documento
- L'
@iddella pagina è unico e coerente? - Il breadcrumb vive nella
WebPageo nell'Article? - Le proprietà di contenimento (
hasPart) sono usate solo quando il contenimento è reale? - I link tematici verso l'esterno usano
significantLink(stringa) ohasPart(oggetto)? - Ogni
@idreferenziato esiste da qualche parte nel grafo? - Il tipo dichiarato è coerente con la realtà della pagina?
- Lo stesso
@idappare in più proprietà con range diversi? Se sì, hai un potenziale conflitto di risoluzione. - Gli array relazionali sono tutti tipizzati o tutti non tipizzati? Se misti, hai un potenziale problema di coerenza interna.
Sull'intero sito
- Ogni pagina usa la stessa convenzione di frammenti?
- I loop di navigazione puntano solo ai contenitori (
#webpage)? - Ci sono
@iddichiarati come due tipi diversi in pagine diverse? - I Dangling Node verso URL esterni sono stati eliminati?
- Le entità master vivono alla radice del
@graphe i riferimenti usano solo@id? - Le eccezioni (pagina che ospita un tool, corso che insegna un concetto) sono gestite con
mainEntityo polimorfia? - Ogni frammento custom (non in whitelist di convenzioni) ha una definizione esplicita nel grafo locale?
- Ogni indice usa
significantLink(nonhasPart) per elencare le sue voci?
Cosa un Audit non può fare
Per essere utile, un metodo deve dichiarare anche i propri limiti.
- Non giudica la strategia editoriale. Verifica che le relazioni siano coerenti con quello che il sito dichiara di essere. Non decide se il sito dovrebbe avere una struttura diversa.
- Non garantisce il rich snippet. Google ha requisiti specifici per i rich results che vanno oltre il vocabolario Schema.org. Un audit può dirti se il markup è ontologicamente corretto, non se Google deciderà di mostrare la stellina.
- Non sostituisce il collaudo umano. Il test linguistico — tradurre il JSON-LD in una frase e verificare se suona forzata — resta un giudizio umano che nessuno strumento può automatizzare completamente.
- Non vede il futuro di Schema.org. Il vocabolario evolve. Un audit fotografa lo stato attuale delle specifiche e delle linee guida Google. Va ripetuto quando qualcosa cambia.
- Non prevede l'evoluzione dei validatori. L'auto-inferenza da frammento, per esempio, non è documentazione ufficiale: è un comportamento osservato dei validator più diffusi. Può cambiare senza preavviso. Un audit basato su di essa resta affidabile, ma va ricontrollato periodicamente.
A chi serve (e a chi no)
Serve davvero a piattaforme editoriali con decine o centinaia di pagine correlate, e-commerce con catalogo strutturato, architetture a topic cluster, siti con template automatici che generano markup a scala, progetti che devono essere letti da AI.
È overkill per siti vetrina da 5-10 pagine statiche, progetti gestiti da plugin standard (Yoast, RankMath) che coprono già Organization, WebSite, WebPage, blog monografici con un solo tipo di contenuto. Su questi progetti, un audit completo è un investimento sproporzionato: il plugin è sufficiente, e la parte ontologica diventa un esercizio teorico senza impatto pratico.
Approfondimenti: teoria & pratica
Questa pagina presenta il metodo dell'audit. Le due guide collegate lo approfondiscono da due angolazioni complementari, sul sito madre IMWEB.
📘 La teoria — Dati Strutturati Avanzati La guida di riferimento sul vocabolario:isPartOf, hasPart, significantLink, ItemList, about, mentions. Spiega il significato ontologico di ogni proprietà, le regole del polimorfismo e del target typing, la parità dei dati, il caso studio sui falsi positivi.
Vai alla guida teorica →
📗 La pratica — Architettura Semantica su Scala
Il seguito pratico: cosa succede quando i pattern corretti teoricamente vengono replicati da un template su centinaia di pagine. Sei "crepe" reali incontrate in produzione, sei trappole dei motori di rendering, un framework di debugging in 4 step (con il Fragment Audit) e un'architettura di separazione dei dati.
Vai alla guida pratica →
Le due guide sono complementari e si rimandano a vicenda. La teoria senza la pratica resta un esercizio intellettuale; la pratica senza la teoria diventa una checklist di regole senza il perché.
FAQ
Basta seguire le specifiche di Schema.org per scrivere JSON-LD corretto?
Seguire le specifiche è la condizione necessaria, non sufficiente. Le specifiche ti dicono quali tipi esistono, quali proprietà accettano, quali range sono validi. Non ti dicono se la relazione che stai dichiarando ha senso per il tuo sito. Un hasPart verso una pagina esterna è sintatticamente valido, ma ontologicamente discutibile. Il validatore passa, il grafo no.
Un audit Schema.org sostituisce il Rich Results Test di Google?
No, lo completa. Il Rich Results Test verifica la compatibilità con i rich snippet di Google: ti dice se il markup è valido secondo gli standard di Search Console e se verrà mostrato un rich result. L'audit semantico verifica un livello diverso: che le relazioni dichiarate siano coerenti, che i nodi siano risolvibili, che il grafo regga. Puoi avere un rich result perfetto e un grafo incoerente — e viceversa.
Cosa sono i Dangling Node e perché un audit dovrebbe scovarli?
Un Dangling Node è un riferimento @id che punta a un'entità che non esiste nel grafo. In teoria dei grafi, un arco che punta al nulla riduce la coerenza del dataset. In pratica, quando un sistema AI costruisce il Knowledge Graph del tuo sito, trova un rimando a un'entità che non riesce a risolvere, e deve indovinare o ignorarlo. Ripetuto su centinaia di pagine, indebolisce la chiarezza del grafo.
Perché due pagine con struttura identica ottengono esiti diversi dal validatore?
Perché il validatore risolve i riferimenti @id in modo contestuale, non isolato. Se lo stesso @id appare in più proprietà con range diversi e non è definito localmente, il validatore lo risolve una volta sola — alla prima occorrenza — e propaga il tipo risultante. Se il tipo è debole (Thing), il declassamento può invalidare proprietà che richiedono range più restrittivi come CreativeWork. Il sintomo appare downstream, in un punto che sembra non correlato.
Ogni quanto va rifatto un Audit Semantico?
Ogni volta che cambia una di queste tre cose: le specifiche Schema.org, le linee guida Google sui rich result, o la struttura del sito. In pratica: un audit completo ogni volta che si aggiunge un nuovo tipo di contenuto, si modifica un template che genera markup, o si aggiunge una nuova sezione al sito. Non è un certificato a vita — è una fotografia dello stato del grafo, va rinnovata.
Serve un audit se uso già un plugin SEO che genera JSON-LD?
Dipende. Un plugin standard genera markup corretto per i casi comuni: Organization, WebSite, WebPage, Article, breadcrumb. Copre la sintassi per la maggior parte delle pagine di un sito normale. Ma non conosce la struttura specifica del sito: non sa quali pagine sono hub, quali sono contenuto singolo, come si collegano tra loro. Su un piccolo sito vetrina, il plugin basta. Su un progetto a topic cluster o un catalogo, serve un audit sopra il plugin.