DocumentazioneScarica

Motore qualità: SEO, accessibilità, performance

Nucleo 0.2+FreeLettura ~27 minAggiornato 31 ottobre 2026

Il motore qualità controlla ogni documento e segnala i problemi di SEO, accessibilità e performance, bloccando la pubblicazione se servono.

In questa pagina

01Panoramica

Il motore qualità controlla un documento Nucleo e restituisce un report con punteggi, problemi e un flag blocking.

Il motore compone il documento come una pagina e ne ricava un profilo: titoli, immagini, link, coppie di colori, landmark, ancore, pulsanti, moduli e stili. Le regole leggono questo profilo, non l'HTML.

Il controllo gira a ogni salvataggio e blocca la pubblicazione se il report è bloccante. Un riconoscimento (acknowledge) rende non bloccante un singolo problema.

La parte SEO stampa title, meta description, Open Graph e JSON-LD sulle pagine singole, ma solo se Yoast SEO e Rank Math non sono attivi.

Tutte le funzioni di questo capitolo sono Free.

02Esecuzione del controllo

Quando gira il controllo

Cosa fa: esegue il controllo sul documento nei seguenti casi:

  • a ogni salvataggio, creazione, patch, pubblicazione e import di un documento, anche in dryRun;
  • alla ricompilazione dopo la modifica di una sezione riutilizzabile;
  • con l'ability nucleo/quality-check;
  • con l'ability nucleo/doc-validate, se il documento è valido;
  • nella scansione del sito (nucleo/quality-scan), per il punteggio SEO di ogni pagina.

Come si usa: non serve un'azione esplicita al salvataggio. Per un controllo senza salvare, usa nucleo/quality-check con document oppure nucleo/doc-validate.

Parametri: Nessuno.

Permessi: quelli dell'operazione che lo avvia.

Edizione: Free.

Limiti noti:

  • Il motore non gira a runtime sul sito pubblico.
  • Il controllo compone un solo documento. Header, footer e layout applicati alla pagina non entrano nel controllo. Le sezioni riutilizzabili (nc/section-ref) invece sì, perché il documento arriva già espanso.
  • Il documento deve superare la validazione dello schema. Se non la supera, nucleo/quality-check restituisce invalid_document senza report.

Profilo della pagina

Cosa fa: percorre la radice e i figli del documento e raccoglie i dati per le regole. Usa la sezione semantics del manifest di ogni blocco (heading, image, link, landmark) e alcuni campi fissi del nodo.

Come si usa: interno. Lo usa il motore qualità.

Parametri:

Dato raccoltoFonte nel nodoUsato da
Titolisemantics.heading.levelFrom (intero 1–6) e textFromseo.h1.*, seo.heading.*, soglia di a11y.contrast
Immaginisemantics.image.srcFrom, altFrom, decorativeFromseo.image.alt.missing, perf.lcp.lazy
Linksemantics.link.hrefFrom, textFrom; content.ariaLabel o settings.ariaLabela11y.link.empty
Landmarksemantics.landmark.tagFrom, oppure il tag fisso con l'eventuale ifFroma11y.landmark.missing
Coppie di coloristyle.color e style.background (per breakpoint)a11y.contrast
Condizionivisibility.when[].isvisibility.logged_in
Ancoresettings.anchor (senza spazi ai bordi)a11y.anchor.duplicate
Posizione fissastyle.position uguale a fixed in almeno un breakpointlayout.fixed.z_index
Pulsantinodi nc/button: content.text, settings.hrefcontent.button.inert
Stiliogni valore stringa di style, anche per breakpointstyle.token.literal
Modulinodi nc/native-form con azioni esterneform.*

Permessi: Nessuno.

Edizione: Free.

Limiti noti:

  • L'immagine LCP stimata è la prima immagine non decorativa. Il profilo la segna con lcp: true e lazy: false. Tutte le altre immagini hanno lazy: true.
  • Il campo visible dei titoli resta sempre vuoto: il profilo non calcola la visibilità per breakpoint.
  • Un nc/button con bind su settings.href non entra nell'elenco dei pulsanti.

Gravità e blocco della pubblicazione

Cosa fa: ogni problema ha una gravità: error, warning o info. Un problema può avere anche publishBlocking. Il report è bloccante se esiste almeno un problema aperto con gravità error o con publishBlocking.

Come si usa: leggi quality.blocking nella risposta.

GravitàBozzaPubblicazione, o salvataggio di un post già pubblicatoPunteggio
errorsi salvafallisce con quality_blocking, salvo acknowledge−20
warning con blocksPublishsi salvafallisce con quality_blocking, salvo acknowledge−5
warningsi salvariesce−5
infosi salvariesce0

Parametri: Nessuno.

Permessi: Nessuno.

Edizione: Free.

Limiti noti:

  • Un salvataggio di un post con stato publish, future o private vale come pubblicazione: il controllo bloccante si applica anche senza publish.
  • Una pagina Nucleo in bozza con un report bloccante non va online neanche dall'editor di WordPress: editor a blocchi, editor classico, pannello della pagina o programmazione. La REST risponde quality_blocking. L'editor classico mantiene lo stato precedente.
  • Alla ricompilazione dopo la modifica di una sezione, un report bloccante su un post pubblicato lascia online la versione precedente. Nucleo conserva i problemi sul post e restituisce quality_blocking. I problemi conservati si cancellano al salvataggio successivo riuscito.
  • Il messaggio di errore è "Blocking quality issues: fix them or acknowledge them.".

Punteggio

Cosa fa: calcola tre punteggi da 0 a 100: seo, a11y e perf. Parte da 100 e toglie 20 per ogni error aperto e 5 per ogni warning aperto. Le info valgono 0. Il minimo è 0.

Come si usa: leggi quality.score.

Parametri: Nessuno.

Permessi: Nessuno.

Edizione: Free.

Limiti noti:

  • La categoria dipende dal prefisso del codice della regola: seo., a11y. o perf.. Le regole con altri prefissi (content., form., style., layout., visibility., links.) non toccano nessun punteggio.
  • I problemi riconosciuti con acknowledge non contano.

Formato del report

Cosa fa: restituisce l'oggetto quality della risposta.

Come si usa:

{
  "quality": {
    "score": { "seo": 75, "a11y": 100, "perf": 100 },
    "blocking": true,
    "issues": [
      {
        "rule": "seo.h1.multiple",
        "severity": "error",
        "nodes": [ { "doc": "page:120", "id": "hero" }, { "doc": "page:120", "id": "sub" } ],
        "message": "The composed page contains 2 h1: hero in page:120, sub in page:120.",
        "fix": [ { "op": "set", "doc": "page:120", "id": "sub", "path": "settings.level", "value": 2 } ]
      }
    ]
  }
}

Parametri:

NomeTipoValori ammessiDefaultNote
rulestringcodice della regola—Da usare in acknowledge.
severitystringerror, warning, info—
nodesarray{doc, id}[]Vuoto per le regole di pagina (seo.h1.missing, a11y.landmark.missing) e per i controlli sul post, che usano {doc: "page:<id>", id: "meta"}.
messagestring——Tradotto, salvo i casi indicati nelle regole.
fixarrayoperazioni di patch con chiave doc in più[]Oggi lo compila solo seo.h1.multiple.
guidestringR1.2, R1.5, R2.3, R4.4, R4.5assentePresente solo per le regole collegate alla guida a fasi.
blocksPublishbooleantrueassentePresente solo per i warning con publishBlocking.

Permessi: Nessuno.

Edizione: Free.

Limiti noti: quality.issues contiene solo i problemi aperti. I problemi riconosciuti non compaiono nella risposta.

Riconoscimento (acknowledge)

Cosa fa: rende non bloccante un problema. Il riconoscimento indica regola, nodo e motivazione. Il motore lo accetta se rule coincide, node è uno degli id in nodes del problema e reason non è vuoto dopo aver tolto gli spazi ai bordi.

Come si usa: passa acknowledge alle ability di scrittura (nucleo/doc-publish, nucleo/doc-patch, nucleo/doc-create, nucleo/site-import) o a nucleo/quality-check. Dalla CLI locale usa nucleo push --acknowledge FILE.

{ "acknowledge": [ { "rule": "form.action.consequential", "node": "f1", "reason": "L'utente ha autorizzato il webhook il 30/10" } ] }

Parametri:

NomeTipoValori ammessiDefaultNote
rulestringcodice della regola—Obbligatorio.
nodestringid di un nodo del problema—Obbligatorio. Un riconoscimento sul nodo nc/section-ref vale per i nodi espansi della sezione.
reasonstringnon vuoto—Obbligatorio.

Permessi: quelli dell'ability che lo riceve.

Edizione: Free.

Limiti noti:

  • Nucleo salva i riconoscimenti usati nel post, con un'impronta (SHA-1) del nodo senza figli. Valgono per i salvataggi, gli import e le ricompilazioni successive.
  • Un riconoscimento decade se il nodo cambia (tipo, contenuto, impostazioni, stile), se il nodo sparisce o se il problema non c'è più.
  • Per la stessa coppia regola e nodo vince il riconoscimento più recente.
  • I problemi senza nodi (seo.h1.missing, a11y.landmark.missing) non si possono riconoscere: restano bloccanti finché non si correggono.
  • Le regole elencate in nonForcible (opzione nucleo_quality, impostabile con nucleo/settings-set nel gruppo quality, fino a 50 id) non si possono riconoscere: restano bloccanti. La lista è vuota di default, quindi ogni regola è forzabile.
  • I riconoscimenti non entrano nel registro attività.

Regole per tipo di documento

Cosa fa: toglie dal report alcune regole quando il documento non è una pagina.

Come si usa: automatico, in base a kind.

kindRegole tolte
layout, popupseo.h1.missing, a11y.landmark.missing, seo.content.thin
sectionle tre precedenti più content.button.inert, form.action.consequential

Parametri: Nessuno.

Permessi: Nessuno.

Edizione: Free.

Limiti noti: nucleo/quality-check aggiunge i controlli sul post solo con postId e solo se kind non è layout, section o popup.

Regole aggiuntive di terze parti

Cosa fa: il filtro nucleo/quality/rules riceve l'elenco delle regole e può aggiungerne o toglierne. Nucleo scarta gli elementi che non implementano l'interfaccia Nucleo\Quality\Rule.

Come si usa:

add_filter('nucleo/quality/rules', function (array $rules): array {
    $rules[] = new MiaRegola(); // implementa id(), severity(), check(ComposedPage $page): array
    return $rules;
});

Parametri: l'interfaccia Rule ha id(): string, severity(): Severity e check(ComposedPage $page): array, cioè una lista di problemi.

Permessi: Nessuno.

Edizione: Free.

Limiti noti: il motore si costruisce una volta per richiesta. Il filtro va aggiunto prima del primo controllo.

03Regole

Tabella riassuntiva. "Punto" indica dove nasce il problema: motore (a ogni salvataggio), sezioni (a ogni salvataggio, sulle sezioni riutilizzabili), post (solo in nucleo/quality-check con postId e nella scansione), sito (solo nella scansione).

CodiceGravitàCategoriaBloccaPuntoCosa controlla
seo.h1.missingerrorseosìmotoreNessun h1
seo.h1.multipleerrorseosìmotorePiù di un h1
seo.heading.skipwarningseonomotoreSalto di livello tra titoli
seo.heading.emptyerrorseosìmotoreTitolo senza testo
seo.image.alt.missingwarningseonomotoreImmagine non decorativa senza alt
a11y.contrasterrora11ysìmotoreContrasto sotto 4.5:1 o 3:1
a11y.link.emptyerrora11ysìmotoreLink senza testo accessibile
a11y.anchor.duplicatewarninga11ynomotoreStessa ancora su più nodi
a11y.landmark.missingwarninga11ynomotoreManca il landmark main
perf.lcp.lazyerrorperfsìmotoreImmagine LCP con loading="lazy"
visibility.logged_ininfo—nomotoreContenuto visibile solo a utenti loggati
layout.fixed.z_indexwarning—nomotoreposition: fixed senza zIndex
content.button.inertwarning + blocksPublish—sìmotorePulsante senza link né azione
form.action.consequentialwarning + blocksPublish—sìmotoreModulo con azioni esterne
form.recipient.invalidwarning—nomotoreDestinatario email del modulo non valido
a11y.button.as-linkwarninga11ynomotoreLink senza destinazione (href vuoto, # o javascript:) o <a> senza href nei testi
a11y.focus.hiddenerrora11ysìmotoreStile di focus che nasconde l'indicatore (outline none, trasparente o alpha 0)
a11y.target.sizewarninga11ynomotorePulsante o icona sotto 24 px nel breakpoint base
a11y.lang.missingwarninga11ynomotoreTesto in inglese su un sito in un'altra lingua, senza settings.lang. Soglia: almeno 8 parole, 3 stopword distinte, almeno il 25%
a11y.video.autoplaywarninga11ynomotoreVideo che parte da solo con l'audio attivo (manca mute=1)
seo.link.text.genericwarningseonomotoreTesto del link generico ("clicca qui", "leggi di più", "here"...) senza aria-label
seo.image.filenameinfoseonomotoreNome file dell'immagine generico (per esempio img, DSC) o esadecimale
seo.heading.style-onlyinfoseonomotoreTesto breve con font grande (24 px o 1.5 rem) senza heading, oppure heading oltre 150 caratteri
seo.hidden.contentwarningseonomotoreTitolo o testo (almeno 20 parole) nascosto su alcuni breakpoint e visibile su altri
perf.html.blockinfoperfnomotoreBlocco nc/html o nc/shortcode: codice libero che il tema non ottimizza
perf.dom.sizewarningperfnomotoreStima degli elementi DOM della pagina sopra 1500 (un avviso per pagina)
perf.css.budgetwarningperfnomotoreCSS servito alla pagina sopra 75 KB
perf.js.budgetwarningperfnomotoreJS servito alla pagina sopra 100 KB
perf.font.countwarningperfnomotorePiù di 2 famiglie o più di 4 varianti di font nel sito (un avviso per sito)
perf.image.oversizewarningperfnomotoreImmagine più del doppio più larga di quanto viene mostrata, senza srcset
perf.embed.no-facadewarningperfnomotoreiframe sopra la piega senza facciata
ds.layout.conflictwarning—nomotoreDue o più layout con la stessa area e le stesse condizioni
style.token.literalerror—sìmotoreValore di stile uguale a un token
content.link.missingwarning—nosezioniRiferimento a un contenuto assente o non pubblicato
content.link.pathinfo—nosezioniLink interno scritto come percorso
content.section.missingwarning—nosezioniSezione riutilizzabile assente o nel cestino
seo.title.lengthwarningseonopostTitle fuori da 30–60 caratteri
seo.description.missingwarningseonopostMeta description assente
seo.description.lengthinfoseonopostMeta description fuori da 70–160 caratteri
seo.content.thinwarningseonopostMeno di 300 parole
seo.noindexwarningseonopostPagina esclusa dai motori di ricerca
seo.canonical.externalwarningseonopostCanonical verso un altro host
seo.links.internal.noneinfoseonopostNessun link interno nel contenuto
seo.title.missingwarningseonopostTitle SEO assente
seo.h1.title-mismatchinfoseonopostPrimo h1 diverso dal title SEO
seo.noindex.importanterrorseosìpostPagina noindex presente nel menu principale
seo.canonical.conflictwarningseonopostCanonical verso un'altra pagina del sito
seo.title.duplicatewarningseonositoStesso title su più pagine
seo.h1.duplicatewarningseonositoStesso testo h1 su più pagine
links.orphanwarning—nositoPagina che nessun altro documento linka
seo.hreflang.missingwarningseonositoPagine senza traduzione in tutte le lingue
seo.schema.duplicateerrorseonositoIl plugin SEO e Nucleo stampano entrambi un BreadcrumbList sulla stessa pagina
seo.duplicate.sectionwarningseonositoSezione ripetuta, visibile solo su schermi piccoli e solo su grandi: contenuto duplicato

seo.h1.missing

Codice: seo.h1.missing. Gravità: error. Categoria: seo.

Cosa controlla: nessun titolo di livello 1 nel documento composto.

Soglie: almeno un h1.

Come si risolve: porta a 1 il livello di un titolo (settings.level per nc/heading).

Limiti noti: il problema non ha nodi, quindi non si può riconoscere. Non vale per layout, section e popup.

seo.h1.multiple

Codice: seo.h1.multiple. Gravità: error. Categoria: seo.

Cosa controlla: più di un titolo di livello 1. Il problema elenca tutti gli h1 in nodes.

Soglie: al massimo un h1.

Come si risolve: applica il fix. Il fix tiene il primo h1 di un documento page: (o il primo in assoluto) e imposta settings.level a 2 sugli altri. In alternativa, riconosci il problema su uno dei nodi.

Limiti noti: se un h1 sta dentro una sezione riutilizzabile, il report toglie il fix e aggiunge "(inside a reusable section)" al messaggio.

seo.heading.skip

Codice: seo.heading.skip. Gravità: warning. Categoria: seo.

Cosa controlla: due titoli consecutivi in ordine di documento in cui il livello sale di più di uno (per esempio h2 seguito da h4). Una discesa di livello non conta.

Soglie: differenza massima +1.

Come si risolve: correggi il livello del secondo titolo.

Limiti noti: il messaggio è fisso in italiano, non tradotto ("Salto di livello: da hX a hY.").

seo.heading.empty

Codice: seo.heading.empty. Gravità: error. Categoria: seo.

Cosa controlla: titolo con testo vuoto dopo la rimozione dei tag e degli spazi ai bordi.

Soglie: nessuna.

Come si risolve: scrivi il testo del titolo o rimuovi il nodo.

Limiti noti: nessuno noto.

seo.image.alt.missing

Codice: seo.image.alt.missing. Gravità: warning. Categoria: seo.

Cosa controlla: immagine non decorativa con alt assente o fatto solo di spazi.

Soglie: nessuna.

Come si risolve: scrivi l'alt, oppure marca l'immagine come decorativa nel campo indicato da semantics.image.decorativeFrom del blocco.

Limiti noti: nessuno noto.

a11y.contrast

Codice: a11y.contrast. Gravità: error. Categoria: a11y.

Cosa controlla: rapporto di contrasto WCAG 2.x tra style.color del nodo e lo sfondo. Lo sfondo è style.background del nodo o dell'antenato più vicino. Senza sfondo dichiarato vale il token $color.background.

Soglie:

  • 4.5:1 per il testo;
  • 3:1 per i titoli di livello 1 e 2;
  • 3:1 per le icone nc/icon con etichetta (content.label non vuota o collegata con bind), secondo WCAG 1.4.11.

Come si risolve: cambia il colore o lo sfondo. Il messaggio riporta i due colori, il rapporto e il minimo.

Limiti noti:

  • Il controllo crea una coppia per ogni breakpoint in cui il colore o lo sfondo cambia. Un breakpoint senza valore eredita il valore base.
  • Accetta solo colori #rgb o #rrggbb, anche come token risolti. Valori come rgba(), var() o gradienti non producono segnalazioni.
  • Non controlla i blocchi senza testo: nc/icon senza etichetta, nc/image, nc/divider, nc/spacer, nc/video, nc/post-image.
  • Per nc/container e nc/section il controllo guarda i figli. Un figlio con colore proprio ha la sua coppia.
  • Il messaggio è fisso in italiano, non tradotto.

Codice: a11y.link.empty. Gravità: error. Categoria: a11y.

Cosa controlla: nodo con semantica di link senza testo visibile e senza ariaLabel in content o settings.

Soglie: nessuna.

Come si risolve: scrivi il testo del link o aggiungi content.ariaLabel.

Limiti noti: nessuno noto.

a11y.anchor.duplicate

Codice: a11y.anchor.duplicate. Gravità: warning. Categoria: a11y.

Cosa controlla: lo stesso valore di settings.anchor su due o più nodi. Un caso tipico è una sezione con ancora usata due volte.

Soglie: un nodo per ancora.

Come si risolve: rendi unica ogni ancora.

Limiti noti: nessuno noto.

a11y.landmark.missing

Codice: a11y.landmark.missing. Gravità: warning. Categoria: a11y.

Cosa controlla: la pagina composta, con i suoi layout, non ha il landmark main, nav o footer.

Soglie: almeno un main, un nav e un footer.

Come si risolve: imposta il tag main sul contenitore principale, nel campo indicato da semantics.landmark.tagFrom. Un nc/menu con un menu è un nav. Di solito il layout footer dà il footer.

Limiti noti: il problema non ha nodi. Non vale per layout, section e popup.

perf.lcp.lazy

Codice: perf.lcp.lazy. Gravità: error. Categoria: perf.

Cosa controlla: immagine segnata come LCP e come lazy.

Soglie: nessuna.

Come si risolve: non serve un intervento sul documento con il profilo attuale.

Limiti noti: il profilo segna sempre l'immagine LCP con lazy: false. Con il motore di Nucleo la regola non produce segnalazioni. Scatta solo con un profilo costruito in altro modo, come nei test.

visibility.logged_in

Codice: visibility.logged_in. Gravità: info. Categoria: nessuna (non entra nei punteggi).

Cosa controlla: nodo con visibility.when che contiene is: "logged_in" o is: "role:<ruolo>". Il contenuto resta nascosto agli anonimi e ai motori di ricerca.

Soglie: nessuna.

Come si risolve: è un'informazione. Verifica che il contenuto non serva all'indicizzazione.

Limiti noti: il messaggio è fisso in italiano, non tradotto.

layout.fixed.z_index

Codice: layout.fixed.z_index. Gravità: warning. Categoria: nessuna.

Cosa controlla: style.position uguale a fixed in almeno un breakpoint, senza la chiave style.zIndex.

Soglie: nessuna.

Come si risolve: aggiungi style.zIndex.

Limiti noti: basta la presenza della chiave zIndex, a qualunque valore.

content.button.inert

Codice: content.button.inert. Gravità: warning con blocksPublish. Categoria: nessuna. Guida: R4.5.

Cosa controlla: nc/button con settings.href vuoto, uguale a # o scartato dal renderer (per esempio javascript:).

Soglie: nessuna.

Come si risolve: aggiungi un href (per esempio page:<slug> o home) oppure usa il blocco interattivo adatto. Il messaggio suggerisce il blocco in base a parole intere del testo, nell'ordine:

Blocco suggeritoParole (radici, maiuscole indifferenti)
nc/slidertesto fatto solo di frecce (← → ‹ › « » < >)
nc/collectionfiltr…, filter(s), ordina…, sort, azzera…, reset, clear, carica altr…, load more, mostra altr…, show more
nc/tabstab(s), scheda, schede
nc/accordionfaq, domand…, espandi, expand, open
nc/sliderprev, previous, next, precedent…, successiv…
nc/searchcerc…, search
nc/native-forminvia…, send, iscriv…, subscribe, contatta…, contact, prenota…, book

Limiti noti:

  • Un href collegato con bind esclude il pulsante dal controllo.
  • Non vale per i documenti section.
  • Un pulsante gestito da uno script tramite attributi non si riconosce. In quel caso serve acknowledge.

form.action.consequential

Codice: form.action.consequential. Gravità: warning con blocksPublish. Categoria: nessuna. Guida: R4.4.

Cosa controlla: nc/native-form con almeno un'azione esterna in settings.actions: webhook, create-post, component, oppure email con to diverso da vuoto e da admin.

Soglie: nessuna.

Come si risolve: chiedi il consenso all'utente. Poi pubblica con acknowledge sul nodo del modulo e scrivi il consenso in reason. Il messaggio chiede anche un invio di prova.

Limiti noti: non vale per i documenti section. Il riconoscimento decade quando il nodo del modulo cambia.

form.recipient.invalid

Codice: form.recipient.invalid. Gravità: warning. Categoria: nessuna.

Cosa controlla: destinatario di un'azione email di nc/native-form che non è admin, un indirizzo email valido o field:<nome>.

Soglie: il <nome> di field: segue ^[a-z][a-z0-9_]{0,31}$.

Come si risolve: usa admin, un indirizzo valido o field:<nome> di un campo email.

Limiti noti: non blocca la pubblicazione.

style.token.literal

Codice: style.token.literal. Gravità: error. Categoria: nessuna. Guida: R1.2.

Cosa controlla: valore stringa di style uguale al valore di un token del gruppo della proprietà (colori, spaziature, raggi, ombre, font). Il confronto è sul valore intero.

Soglie: un problema per nodo, proprietà e valore, anche se il valore si ripete nei breakpoint.

Come si risolve: scrivi il riferimento al token indicato nel messaggio (per esempio $color.primary). Dalla CLI locale, nucleo push --use-tokens sostituisce i valori nei file.

Limiti noti: la regola è attiva solo se il motore riceve i token del sito. Nucleo li passa sempre.

Codici e gravità: content.link.missing (warning, guida R2.3), content.link.path (info, guida R2.3), content.section.missing (warning, guida R1.5). Categoria: nessuna.

Cosa controllano:

  • content.link.missing: un riferimento interno (per esempio page:contatti) punta a un contenuto assente o non pubblicato. Il link esce come # con aria-disabled.
  • content.link.path: un link interno scritto come percorso (/contatti) ha un riferimento equivalente.
  • content.section.missing: un nc/section-ref cita una sezione assente o nel cestino. Il blocco non disegna nulla.

Soglie: nessuna.

Come si risolvono: pubblica il contenuto o correggi il riferimento; scrivi il riferimento suggerito nel messaggio; crea o ripristina la sezione.

Limiti noti: non nascono dalle regole del motore e non passano dal filtro nucleo/quality/rules. Nucleo li aggiunge a ogni salvataggio, in nucleo/quality-check e in nucleo/doc-validate.

seo.title.length

Codice: seo.title.length. Gravità: warning. Categoria: seo.

Cosa controlla: lunghezza in caratteri del title SEO letto da Nucleo (vedi Lettura dei meta SEO).

Soglie: da 30 a 60 caratteri.

Come si risolve: senza plugin SEO, scrivi meta.seo.title del documento. Con Yoast SEO o Rank Math, modifica il title nel plugin.

Limiti noti: senza plugin SEO e senza meta.seo.title, conta il titolo del post. Il problema usa il nodo meta del documento page:<id>.

seo.description.missing, seo.description.length

Codici: seo.description.missing (gravità warning), seo.description.length (gravità info). Categoria: seo.

Cosa controllano: meta description assente; altrimenti lunghezza fuori soglia.

Soglie: da 70 a 160 caratteri.

Come si risolvono: scrivi meta.seo.description, oppure la description nel plugin SEO attivo.

Limiti noti: lo schema del documento accetta meta.seo.description fino a 170 caratteri, oltre la soglia consigliata.

seo.content.thin

Codice: seo.content.thin. Gravità: warning. Categoria: seo.

Cosa controlla: numero di parole del contenuto della pagina (HTML compilato) senza tag, con le entità decodificate.

Soglie: almeno 300 parole. Valore fisso.

Come si risolve: aggiungi testo.

Limiti noti: la soglia non cambia per tipo di contenuto. Non vale per layout, section e popup.

seo.noindex

Codice: seo.noindex. Gravità: warning. Categoria: seo.

Cosa controlla: il valore noindex letto da Nucleo (vedi Lettura dei meta SEO).

Soglie: nessuna.

Come si risolve: togli il noindex nel plugin SEO, o nelle impostazioni di lettura di WordPress.

Limiti noti: senza plugin SEO, noindex vale vero se l'opzione blog_public è '0' (sito che scoraggia i motori di ricerca). meta.seo.noindex del documento non attiva la regola. Con Yoast SEO la regola legge il noindex di Yoast; con Rank Math legge il robots di Rank Math.

seo.canonical.external

Codice: seo.canonical.external. Gravità: warning. Categoria: seo.

Cosa controlla: host del canonical diverso dall'host del sito, senza distinzione tra maiuscole e minuscole.

Soglie: nessuna.

Come si risolve: correggi il canonical nel plugin SEO.

Limiti noti: senza plugin SEO il canonical letto è sempre vuoto: la regola scatta solo con Yoast SEO o Rank Math.

Codice: seo.links.internal.none. Gravità: info. Categoria: seo (vale 0 nel punteggio).

Cosa controlla: nessun link interno nel contenuto della pagina. Interno significa stesso host del sito, oppure href senza host e senza schema.

Soglie: almeno un link interno.

Come si risolve: aggiungi un link a un'altra pagina del sito.

Limiti noti: un href #ancora o relativo conta come interno.

seo.title.duplicate, seo.h1.duplicate

Codici: seo.title.duplicate, seo.h1.duplicate. Gravità: warning. Categoria: seo.

Cosa controllano: stesso title SEO, o stesso testo di un h1, su due o più documenti pubblicati. Il confronto avviene dopo minuscole e riduzione degli spazi.

Soglie: due pagine.

Come si risolvono: rendi unici title e h1.

Limiti noti: solo nella scansione del sito. L'h1 si legge dall'HTML del contenuto. Il problema elenca le pagine coinvolte (lista di ID) invece dei nodi.

Codice: links.orphan. Gravità: warning. Categoria: nessuna.

Cosa controlla: documento pubblicato che nessun altro documento pubblicato linka. La home statica è esclusa.

Soglie: almeno un link in ingresso.

Come si risolve: aggiungi un link alla pagina da un altro documento.

Limiti noti: legge solo i link nell'HTML dei documenti Nucleo pubblicati. Menu, header e footer non contano. Accetta solo href che iniziano con / o con l'indirizzo della home; ignora gli href che iniziano con //.

seo.hreflang.missing

Codice: seo.hreflang.missing. Gravità: warning. Categoria: seo.

Cosa controlla: documenti pubblicati a cui manca la traduzione in almeno una lingua del sito. Un solo problema elenca tutte le pagine.

Soglie: si attiva con almeno 2 lingue.

Come si risolve: crea le traduzioni mancanti nel plugin multilingua.

Limiti noti: le lingue vengono dal plugin multilingua supportato e dai filtri nucleo/languages e nucleo/translations.

04Scansione del sito

Scansione a lotti

Cosa fa: legge tutti i post con stato publish e un documento Nucleo, di ogni tipo di contenuto, in ordine di ID. Calcola le regole di sito su tutti i documenti a ogni chiamata. Calcola i punteggi solo per le pagine del lotto richiesto.

Come si usa: ability nucleo/quality-scan con page. Ripeti la chiamata con page: next finché next è null. Esito:

{
  "site": [ { "rule": "links.orphan", "severity": "warning", "message": "Page without internal links from other documents.", "posts": [42] } ],
  "pages": [ { "postId": 42, "score": 85, "a11y": 100, "perf": 100 } ],
  "next": 2
}

Parametri:

NomeTipoValori ammessiDefaultNote
pageinteger≥ 11Numero del lotto.

Permessi: edit_posts. Scope OAuth nucleo:read.

Edizione: Free.

Limiti noti:

  • Il lotto è di 50 pagine. Il parametro di dimensione del lotto non è esposto dall'ability.
  • Ogni pagina riporta i punteggi seo, a11y e perf. Somma le regole del motore e dei controlli sul post. Non applica acknowledge né la limitazione per tipo di documento.
  • Con page: 1 l'ability salva per un giorno, nel transient nucleo_quality_badge, il numero di pagine coinvolte da problemi di sito con gravità error. I problemi di sito sono warning, tranne seo.schema.duplicate, che è error.

Controllo di un documento (nucleo/quality-check)

Cosa fa: restituisce il report di un documento salvato (postId) o passato in input (document), senza scrivere.

Come si usa: vedi l'ability nucleo/quality-check.

Parametri: postId (intero ≥ 1) oppure document (oggetto), mai tutti e due; acknowledge (lista di {rule, node, reason}).

Permessi: con postId, edit_post su quel post; senza, edit_posts. Scope OAuth nucleo:read.

Edizione: Free.

Limiti noti:

  • I riconoscimenti salvati nel post non entrano: contano solo quelli passati nella chiamata.
  • I controlli sul post girano solo con postId.
  • Errori: invalid_input (nessuno o entrambi i parametri), not_found, invalid_document.

Pagina admin Quality and SEO

Cosa fa: la pagina esegue la scansione a lotti, mostra i problemi di sito, i punteggi delle pagine e il dettaglio di una pagina con nucleo/quality-check.

Come si usa: menu Nucleo AI → Quality and SEO.

Parametri: schede issues, pages, overrides, rules.

Permessi: pagina edit_posts; avvio della scansione edit_others_posts. Schede issues e overrides: edit_others_posts. Scheda rules: edit_theme_options. Il contatore nel menu compare solo con edit_others_posts.

Edizione: Free.

Limiti noti:

  • La scheda overrides mostra "Not available yet".
  • Le colonne Accessibility e Performance della scheda pages mostrano i punteggi a11y e perf della scansione. Un valore assente resta "—".
  • Con la scansione, la scheda rules mostra la gravità reale di ogni regola. Senza scansione mostra una lista fissa di dieci regole con una gravità "tipica", che per quattro non coincide con quella del codice: seo.image.alt.missing (error invece di warning), perf.lcp.lazy (warning invece di error), seo.noindex (info invece di warning), seo.links.internal.none (warning invece di info).

05SEO in pagina

Lettura dei meta SEO

Cosa fa: restituisce title, description, source, noindex e canonical di un post. Con Yoast SEO attivo legge Yoast; con Rank Math attivo legge Rank Math; altrimenti legge meta.seo del documento.

Come si usa: interno. Lo usano i controlli sul post e la scansione del sito. Il filtro nucleo/seo/meta può cambiare il risultato.

Parametri: Nessuno.

Permessi: Nessuno.

Edizione: Free.

Limiti noti: senza plugin SEO, canonical è sempre vuoto e noindex segue l'impostazione di WordPress che scoraggia i motori di ricerca (blog_public).

Title, meta description, Open Graph, robots

Cosa fa: sulle pagine singole con documento Nucleo e meta.seo non vuoto, sostituisce il title del documento HTML e stampa nel wp_head, con priorità 1, questi tag:

TagValoreQuando
<meta name="description">meta.seo.descriptionse non vuoto
og:titlemeta.seo.title, altrimenti titolo del postsempre
og:descriptionmeta.seo.descriptionse non vuoto
og:urlpermalinksempre
og:typewebsitesempre
og:imagemeta.seo.imagese non vuoto
<meta name="robots" content="noindex">—se meta.seo.noindex è true

Come si usa: compila meta.seo nel documento.

NomeTipoValori ammessiDefaultNote
meta.seo.titlestringmax 70 caratterititolo del post
meta.seo.descriptionstringmax 170 caratteri—
meta.seo.imagestringURI—
meta.seo.noindexbooleanfalseEsclude anche dalla sitemap.

Permessi: Nessuno (frontend).

Edizione: Free.

Limiti noti:

  • Si spegne se Yoast SEO o Rank Math è attivo.
  • Il canonical resta a WordPress: Nucleo non lo stampa.
  • Nessun tag Twitter Card. og:type è sempre website.
  • Archivi e home del blog non ricevono questi tag: valgono solo sulle pagine singole.

JSON-LD della pagina

Cosa fa: stampa nel footer della pagina un solo script application/ld+json con @context e @graph. Il grafo contiene, in ordine:

  1. i nodi di meta.seo.schema, con url uguale al permalink se manca; il valore "@business", a ogni profondità, diventa {"@id": "<home>/#business"};
  2. il nodo dell'azienda, sulla home statica o se un nodo usa "@business";
  3. i nodi degli script ld+json presenti nel contenuto, tolti dall'HTML; uno script con @graph aggiunge ogni nodo del proprio grafo;
  4. un BreadcrumbList automatico.

Come si usa: compila meta.seo.schema e, se serve, meta.seo.breadcrumbs.

NomeTipoValori ammessiDefaultNote
meta.seo.schemaarray di oggettimax 10 nodi; @type obbligatorio con pattern ^[A-Z][A-Za-z]{2,60}$[]
meta.seo.breadcrumbsbooleanfalse—false toglie il breadcrumb automatico.

Permessi: Nessuno (frontend).

Edizione: Free.

Limiti noti:

  • Si spegne se Yoast SEO o Rank Math è attivo.
  • Gira su ogni pagina singola, anche senza meta.seo: il grafo può contenere solo il breadcrumb.
  • Breadcrumb: nome del sito, antenati del post, pagina. Manca sulla home statica e se il contenuto contiene già un BreadcrumbList.
  • Nucleo unisce i nodi: toglie i nodi identici e fonde tutte le FAQPage in una sola.
  • Gli script con JSON non valido restano nel contenuto.
  • L'output escapa < e >: un </script> nei testi non chiude lo script.

Consolidamento JSON-LD alla compilazione

Cosa fa: alla compilazione del documento toglie dall'HTML gli script ld+json validi e aggiunge in fondo un solo script con @graph. Fonde le FAQPage (per esempio due nc/accordion) e toglie i nodi identici.

Come si usa: automatico, alla compilazione. Il filtro nucleo/schema/skip_types riceve la lista dei @type da escludere.

Parametri: Nessuno.

Permessi: Nessuno.

Edizione: Free.

Limiti noti:

  • Con Yoast SEO o Rank Math attivo, la lista di default esclude WebPage, WebSite, Organization e BreadcrumbList. Senza plugin SEO la lista è vuota.
  • L'esclusione vale al momento della compilazione. Se attivi un plugin SEO dopo, i documenti già compilati restano invariati fino al salvataggio successivo.
  • Gli script con JSON non valido o con una lista al primo livello restano dove sono.

Dati dell'azienda

Cosa fa: salva nell'opzione nucleo_business i dati dell'azienda e ne ricava il nodo schema.org per il JSON-LD. Il nodo ha @id <home>/#business, url di default la home, address con @type: PostalAddress e geo con @type: GeoCoordinates.

Come si usa: chiave business di nucleo/site-options-set. null cancella i dati.

NomeTipoValori ammessiDefaultNote
namestring1–300 caratteri—Obbligatorio.
@typestring^[A-Z][A-Za-z]{2,60}$LocalBusiness
legalName, vatID, taxID, description, telephone, email, priceRangestring1–300 caratteri—
url, logo, imagestringURL http o httpsurl = home
sameAsarrayURL http o https—Una stringa singola non vale.
addressobjectstreetAddress, addressLocality, addressRegion, postalCode, addressCountry (stringhe)—Altre chiavi rifiutate.
geoobjectlatitude (±90) e longitude (±180), numeri—Entrambe obbligatorie, nessun'altra chiave.
openingHoursarrayMo-Fr 08:00-18:00 (giorni Mo…Su, ore 00–23)—
foundingDatestringAAAA, AAAA-MM o AAAA-MM-GG—
areaServedstring o array di stringhe1–300 caratteri—
altre chiavicamelCase ^[a-z][A-Za-z]{1,40}$testo, lista di testi o oggetto di scalari—

Permessi: manage_options. Scope OAuth nucleo:admin.

Edizione: Free.

Limiti noti:

  • Un valore non valido restituisce invalid_input con il nome della chiave: "<chiave>" is unknown or has an invalid value..
  • Il nodo esce solo dove il JSON-LD di Nucleo è attivo, quindi non con Yoast SEO o Rank Math.

Sitemap

Cosa fa: Nucleo esclude dalla sitemap di WordPress i post con meta.seo.noindex impostato a true. Aggiorna l'esclusione a ogni salvataggio e ricompilazione.

Come si usa: imposta meta.seo.noindex: true nel documento.

Parametri: Nessuno.

Permessi: Nessuno.

Edizione: Free.

Limiti noti:

  • L'esclusione vale solo sulle richieste pubbliche della sitemap, e solo senza Yoast SEO o Rank Math.
  • Nucleo non genera una propria sitemap e non modifica robots.txt. Il solo controllo sui robot è il meta noindex della pagina.

Coesistenza con Yoast SEO e Rank Math

Cosa fa: Nucleo rileva il plugin SEO attivo. Con un plugin attivo:

  • Nucleo non stampa title, description, Open Graph né robots;
  • non stampa il JSON-LD della pagina e non toglie gli script dal contenuto;
  • nel JSON-LD compilato esclude WebPage, WebSite, Organization e BreadcrumbList;
  • non applica l'esclusione dalla sitemap;
  • i controlli sul post e la scansione leggono title, description, noindex e canonical dal plugin.

Come si usa: automatico.

Parametri: Nessuno.

Permessi: Nessuno.

Edizione: Free.

Limiti noti: se entrambi i plugin sono attivi, vince Yoast SEO.

Esc
↑↓ per scegliere↵ per aprireEsc per chiudereRicerca locale, nessun dato inviato