TL;DR
Il file theme.liquid del tuo tema Shopify controlla la sezione <head> di ogni pagina — quindi controlla anche come i motori di ricerca vedono l’intero store. Ho raccolto 10 ottimizzazioni che possono portare un tema Dawn da un punteggio Lighthouse di 65 a 90+. Ognuna include codice Liquid da copiare e incollare, una spiegazione di cosa fa e cosa può andare storto. Tempo totale di implementazione: 2-4 ore.
Ho analizzato centinaia di store Shopify tramite SEOJuice. Lo schema è sempre lo stesso: il proprietario sceglie un tema, personalizza i colori, carica i prodotti e non tocca mai theme.liquid. Nel frattempo, il concorrente con gli stessi prodotti e la metà dei backlink lo supera in classifica perché il suo sviluppatore ha passato un pomeriggio a ottimizzare il codice del tema.
Il tema Shopify Dawn ottiene 92 su PageSpeed appena installato — il tema gratuito più veloce disponibile su Shopify. Ma “appena installato” lascia comunque parecchie opportunità SEO sul tavolo. Mancano schema markup completi. Il caricamento dei font non è ottimizzato. Nessun hint di preconnect. Tag OG incompleti.
Questa guida sistema tutto questo. E la prossima sezione — i canonical tag — è quella che vorrei che ogni proprietario di store Shopify capisse prima di arrivare da me chiedendosi perché Google stesse indicizzando la versione sbagliata delle sue pagine prodotto.
Se non sei uno sviluppatore, ecco la versione da 30 secondi: theme.liquid è il file template principale che avvolge ogni singola pagina del tuo store Shopify. Definisce la struttura di <html>, <head> e <body>. Ogni pagina prodotto, pagina collezione, articolo del blog e homepage eredita da questo file.
Quando aggiungi qualcosa alla sezione <head> di theme.liquid, quella cosa appare su ogni pagina. Questo lo rende il posto perfetto per:
Per modificarlo: Shopify Admin → Online Store → Themes → Edit Code → Layout → theme.liquid
Punto chiave
Duplica sempre il tema prima di modificarlo. Vai su Online Store → Themes → Actions → Duplicate. Lavora sulla copia. Se qualcosa si rompe, puoi tornare indietro all’istante.
Ecco cosa implementiamo, dove va inserito e cosa ti porta in pratica:
| Ottimizzazione | Dove aggiungerla | Impatto SEO | Difficoltà |
|---|---|---|---|
| Canonical tag | theme.liquid <head> | Alta — previene contenuti duplicati | Facile |
| Schema Organization | theme.liquid <head> | Media — knowledge panel del brand | Facile |
| Schema Product | product.liquid o section | Alta — rich results nelle SERP | Media |
| Schema Breadcrumb | Snippet + template | Media — breadcrumb nelle SERP | Media |
| Preload dei font | theme.liquid <head> | Alta — miglioramento LCP | Facile |
| Inlining del CSS critico | theme.liquid <head> | Alta — elimina il render blocking | Difficile |
| Defer del JS non critico | theme.liquid <body> fine | Alta — parsing DOM più veloce | Media |
| Meta tag OG/Twitter | theme.liquid <head> | Media — condivisione social | Facile |
| Tag hreflang | theme.liquid <head> | Alta — SEO internazionale | Media |
| Lazy loading delle immagini | Templates/sections | Media — riduce il payload iniziale | Facile |
Shopify genera molte URL duplicate. Un prodotto può essere raggiunto tramite /products/widget e /collections/sale/products/widget — stesso prodotto, due URL. Senza un canonical tag, Google vede contenuto duplicato e può indicizzare quella sbagliata. Ho visto store in cui Google indicizzava la URL con il percorso della collection per ogni singolo prodotto, il che significava che le URL prodotto “vere” avevano autorità zero.
Dawn include una gestione base dei canonical, ma vale la pena verificarla e migliorarla. Aggiungi questo nella sezione <head> di theme.liquid:
{%- liquid
assign canonical_url = canonical_url | default: request.origin | append: request.path
-%}
<link rel="canonical" href="{{ canonical_url }}">
{%- if paginate and paginate.pages > 1 -%}
{%- if paginate.previous -%}
<link rel="prev" href="{{ paginate.previous.url }}">
{%- endif -%}
{%- if paginate.next -%}
<link rel="next" href="{{ paginate.next.url }}">
{%- endif -%}
{%- endif -%}
Questo fa tre cose: imposta la canonical URL usando la variabile integrata di Shopify canonical_url (che risolve la URL corretta per i prodotti indipendentemente da come vengono raggiunti), aggiunge rel="prev"/rel="next" per le collection paginate e usa come fallback l’origine della request + il path se canonical_url non è disponibile.
Punto chiave
Controlla che il tuo tema non stia già stampando un canonical tag prima di aggiungere questo. I canonical duplicati confondono i motori di ricerca più dell’assenza totale. Cerca prima “canonical” nel file theme.liquid.
Lo schema Organization dice a Google chi sei — nome dell’azienda, logo, profili social e informazioni di contatto. È ciò che alimenta il knowledge panel che appare quando qualcuno cerca il nome del tuo brand. Ho visto store passare da nessun knowledge panel a uno completamente popolato nel giro di tre settimane dopo aver aggiunto correttamente questo markup.
Aggiungilo dentro <head> in theme.liquid. Viene eseguito su ogni pagina, che è esattamente ciò che Google vuole:
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Organization",
"name": {{ shop.name | json }},
"url": "{{ shop.url }}",
{%- if shop.brand.logo -%}
"logo": {{ shop.brand.logo | image_url: width: 600 | json }},
{%- endif -%}
"description": {{ shop.description | json }},
"address": {
"@type": "PostalAddress",
"addressCountry": {{ shop.address.country | json }}
}
{%- if shop.brand.social_links.size > 0 -%}
,"sameAs": [
{%- for link in shop.brand.social_links -%}
{{ link | json }}{%- unless forloop.last -%},{%- endunless -%}
{%- endfor -%}
]
{%- endif -%}
}
</script>
Nota che sto usando il filtro | json invece di | escape. È importante — json esegue l’escaping corretto delle stringhe nel contesto JSON-LD, compreso l’inserimento delle virgolette. Usare escape nel JSON-LD è un errore comune che produce markup non valido. Ho corretto questo problema specifico in almeno una dozzina di store clienti — lo schema risulta valido nel tool di test di Google ma fallisce silenziosamente in produzione perché l’escaping è sbagliato.
"JSON-LD è il formato preferito da Google per i dati strutturati. Se stai implementando schema markup su Shopify, scegli sempre JSON-LD invece dei microdata — è più pulito, più facile da debuggare e non si mescola con il tuo HTML di presentazione."
Lo schema Product è ciò che ti fa ottenere quei rich results su Google — stelline, prezzo, disponibilità. Dawn include uno schema prodotto di base, ma spesso è incompleto. Questa è una versione più completa che ho affinato testandola su decine di store.
Crea un nuovo snippet: snippets/json-ld-product.liquid
{%- if product -%}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "Product",
"name": {{ product.title | json }},
"url": "{{ shop.url }}{{ product.url }}",
"description": {{ product.description | strip_html | truncate: 500 | json }},
{%- if product.featured_image -%}
"image": [
{{ product.featured_image | image_url: width: 1200 | json }}
{%- for image in product.images offset: 1 limit: 4 -%}
,{{ image | image_url: width: 1200 | json }}
{%- endfor -%}
],
{%- endif -%}
"brand": {
"@type": "Brand",
"name": {{ product.vendor | json }}
},
{%- if product.selected_or_first_available_variant.sku != blank -%}
"sku": {{ product.selected_or_first_available_variant.sku | json }},
{%- endif -%}
{%- if product.selected_or_first_available_variant.barcode != blank -%}
"gtin": {{ product.selected_or_first_available_variant.barcode | json }},
{%- endif -%}
"offers": {
"@type": "AggregateOffer",
"priceCurrency": {{ cart.currency.iso_code | json }},
"lowPrice": {{ product.price_min | money_without_currency | json }},
"highPrice": {{ product.price_max | money_without_currency | json }},
"offerCount": {{ product.variants.size }},
"availability": "https://schema.org/{% if product.available %}InStock{% else %}OutOfStock{% endif %}",
"url": "{{ shop.url }}{{ product.url }}"
}
{%- if product.metafields.reviews.rating.value != blank -%}
,"aggregateRating": {
"@type": "AggregateRating",
"ratingValue": {{ product.metafields.reviews.rating.value | json }},
"reviewCount": {{ product.metafields.reviews.rating_count.value | json }}
}
{%- endif -%}
}
</script>
{%- endif -%}
Poi includilo nel template prodotto (di solito sections/main-product.liquid o templates/product.liquid):
{% render 'json-ld-product' %}
Dettagli importanti: sto usando AggregateOffer invece di singoli oggetti Offer perché è più pulito per prodotti con più varianti. I campi rating usano i metafield nativi di Shopify per le recensioni prodotto — se usi un’app di recensioni di terze parti, dovrai adattare il namespace dei metafield. (Judge.me e Loox usano namespace diversi — controlla la loro documentazione prima di copiare questo codice alla cieca.)
I breadcrumb aiutano Google a capire la gerarchia del sito e possono apparire direttamente nei risultati di ricerca, sostituendo la URL grezza. È particolarmente utile per store con strutture di categoria profonde. In uno store che ho analizzato, aggiungere lo schema breadcrumb ha cambiato la visualizzazione in SERP da example.com › collections › summer-2025 › products › red-dress a un pulito Home > Collezione Estiva > Abito Rosso — e il CTR è migliorato in modo misurabile.
Crea snippets/json-ld-breadcrumbs.liquid:
{%- assign breadcrumb_list = "" | split: "" -%}
{%- if template.name == 'product' -%}
{%- if product.collections.size > 0 -%}
{%- assign primary_collection = product.collections | first -%}
{%- capture crumb_collection -%}{"name":{{ primary_collection.title | json }},"url":"{{ shop.url }}{{ primary_collection.url }}"}{%- endcapture -%}
{%- assign breadcrumb_list = breadcrumb_list | push: crumb_collection -%}
{%- endif -%}
{%- capture crumb_product -%}{"name":{{ product.title | json }},"url":"{{ shop.url }}{{ product.url }}"}{%- endcapture -%}
{%- assign breadcrumb_list = breadcrumb_list | push: crumb_product -%}
{%- elsif template.name == 'collection' -%}
{%- capture crumb_coll -%}{"name":{{ collection.title | json }},"url":"{{ shop.url }}{{ collection.url }}"}{%- endcapture -%}
{%- assign breadcrumb_list = breadcrumb_list | push: crumb_coll -%}
{%- elsif template.name == 'article' -%}
{%- capture crumb_blog -%}{"name":{{ blog.title | json }},"url":"{{ shop.url }}{{ blog.url }}"}{%- endcapture -%}
{%- assign breadcrumb_list = breadcrumb_list | push: crumb_blog -%}
{%- capture crumb_article -%}{"name":{{ article.title | json }},"url":"{{ shop.url }}{{ article.url }}"}{%- endcapture -%}
{%- assign breadcrumb_list = breadcrumb_list | push: crumb_article -%}
{%- endif -%}
{%- if breadcrumb_list.size > 0 -%}
<script type="application/ld+json">
{
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": [
{
"@type": "ListItem",
"position": 1,
"name": "Home",
"item": "{{ shop.url }}"
}
{%- for crumb_json in breadcrumb_list -%}
,{
"@type": "ListItem",
"position": {{ forloop.index | plus: 1 }},
"name": {{ crumb_json | split: '"name":' | last | split: ',"url"' | first }},
"item": {{ crumb_json | split: '"url":' | last | split: '}' | first }}
}
{%- endfor -%}
]
}
</script>
{%- endif -%}
Aggiungi {% render 'json-ld-breadcrumbs' %} nel tuo theme.liquid subito prima di </head>. Lo snippet rileva automaticamente il tipo di pagina e costruisce la catena breadcrumb corretta.
Questa è la modifica che ha fatto la differenza più grande nei nostri store di test. Il solo preload dei font può togliere un secondo pieno al time-to-text — e nell’e-commerce quel secondo è direttamente correlato al bounce rate. Il team engineering di Shopify ha mostrato l’impatto direttamente su shopify.com:
"Con preload, gli sviluppatori front-end possono dire al browser di scaricare i file font necessari nello stesso momento in cui scarica i file CSS. Solo 17 righe di codice hanno ridotto del 50% il tempo necessario a mostrare il testo su shopify.com."
Il problema: i browser non possono scaricare i font finché non hanno analizzato sia l’HTML sia il CSS che li richiama. Sono due round trip sequenziali prima che il testo appaia. Il preload dice al browser: “inizia a scaricare questo font adesso, ti servirà tra poco”.
Aggiungi questo vicino all’inizio di <head> in theme.liquid, prima dei tag del foglio di stile:
{%- comment -%}
Preload your primary heading and body fonts.
Check your theme's font settings to get the correct filenames.
Use font-display: swap in your CSS to prevent FOIT (Flash of Invisible Text).
{%- endcomment -%}
{%- if settings.type_header_font != blank -%}
<link rel="preload"
href="{{ settings.type_header_font | font_url }}"
as="font"
type="font/woff2"
crossorigin>
{%- endif -%}
{%- if settings.type_body_font != blank -%}
<link rel="preload"
href="{{ settings.type_body_font | font_url }}"
as="font"
type="font/woff2"
crossorigin>
{%- endif -%}
{%- comment -%} Preconnect to Shopify's CDN for faster asset loading {%- endcomment -%}
<link rel="preconnect" href="https://cdn.shopify.com" crossorigin>
<link rel="preconnect" href="https://fonts.shopifycdn.com" crossorigin>
L’attributo crossorigin è obbligatorio per il preload dei font — senza, il browser scarica il font due volte. Ho visto questo errore esatto su store in produzione, dove eliminare il download duplicato da solo ha migliorato l’LCP di 400ms. Gli hint di preconnect fanno risparmiare altri ~100ms stabilendo in anticipo la connessione TCP + TLS verso il CDN di Shopify.
Punto chiave
Fai preload solo dei font che usi above the fold. Ogni preload compete per la banda. Se fai preload di cinque font, hai annullato il vantaggio — il browser li scaricherà tutti e cinque in parallelo, rallentando tutto il resto.
Il CSS che blocca il rendering è il motivo numero 1 per cui gli store Shopify ottengono brutti punteggi su Lighthouse. Il browser non può disegnare nulla finché non scarica e analizza l’intero stylesheet — anche il CSS del footer che sta 3,000px sotto la piega. Questa è l’ottimizzazione più difficile della lista, ma è anche quella che nei miei audit ha prodotto più spesso il miglior salto su Lighthouse.
La soluzione: inserire inline il CSS necessario per il contenuto above the fold direttamente dentro <head> e caricare il resto in modo asincrono.
{%- comment -%}
Step 1: Inline critical CSS directly in the head.
Generate your critical CSS using a tool like our
Critical CSS Generator at /tools/critical-css-generator/
Then paste it into a snippet: snippets/critical-css.liquid
{%- endcomment -%}
<style>
{% render 'critical-css' %}
</style>
{%- comment -%}
Step 2: Load full stylesheet asynchronously using the media trick.
The browser treats media="print" as non-render-blocking,
then onload switches it to media="all" so styles apply.
{%- endcomment -%}
<link rel="stylesheet"
href="{{ 'base.css' | asset_url }}"
media="print"
onload="this.media='all'">
<noscript>
<link rel="stylesheet" href="{{ 'base.css' | asset_url }}">
</noscript>
In alternativa, Shopify fornisce il filtro Liquid inline_asset_content che può inserire inline il CSS direttamente dai file asset, eliminando la necessità di uno snippet separato:
<style>{{ 'critical.css' | asset_url | inline_asset_content }}</style>
Generare correttamente il CSS critico richiede l’analisi di ogni template. Ti consiglio di usare uno strumento per generare Critical CSS — estrae esattamente le regole CSS necessarie per il contenuto above the fold su ogni tipo di pagina. Se sbagli qui (per esempio manca una regola del tuo hero), avrai un flash di contenuto non stilizzato che è peggio di non ottimizzare affatto.
Gli script che bloccano il parser sono il secondo killer delle performance dopo il CSS che blocca il rendering. La guida ufficiale di Shopify è chiara: il tuo bundle JS minificato dovrebbe essere di 16KB o meno, e tutto il resto dovrebbe essere deferito.
In theme.liquid, trova i tuoi tag <script> e aggiungi defer:
{%- comment -%}
WRONG — blocks DOM parsing:
{{ 'theme.js' | asset_url | script_tag }}
RIGHT — deferred loading:
{%- endcomment -%}
<script src="{{ 'theme.js' | asset_url }}" defer></script>
{%- comment -%}
For third-party scripts (analytics, chat widgets, etc.),
load them after the page is interactive:
{%- endcomment -%}
<script>
// Load non-essential scripts after user interaction
function loadDeferredScripts() {
// Example: chat widget, analytics, etc.
var scripts = [
'{{ "deferred-features.js" | asset_url }}'
];
scripts.forEach(function(src) {
var s = document.createElement('script');
s.src = src;
s.defer = true;
document.body.appendChild(s);
});
}
// Trigger on first user interaction
['click', 'scroll', 'keydown', 'touchstart'].forEach(function(evt) {
window.addEventListener(evt, function handler() {
loadDeferredScripts();
['click', 'scroll', 'keydown', 'touchstart'].forEach(function(e) {
window.removeEventListener(e, handler);
});
}, { once: true, passive: true });
});
</script>
Il pattern di caricamento basato sull’interazione è aggressivo ma efficace. Chat widget, popup recensioni e funzioni simili non devono caricarsi finché l’utente non fa davvero qualcosa sulla pagina. In uno store che abbiamo testato, questo pattern ha tolto 340ms dal Time to Interactive — abbastanza da passare da un punteggio INP “needs improvement” a “good” nei dati CrUX.
Quando qualcuno condivide il tuo prodotto su Facebook, Twitter o LinkedIn, i tag OG (Open Graph) controllano ciò che appare nell’anteprima. Senza, le piattaforme social vanno a tentativi — e di solito sbagliano male. Ho visto condivisioni prodotto apparire con il favicon dello store come immagine e il title della pagina checkout come descrizione.
Dawn include uno snippet social-meta-tags.liquid, ma spesso è minimale. Qui sotto trovi una versione più completa. Crea o aggiorna snippets/social-meta-tags.liquid:
{%- liquid
assign og_title = page_title | default: shop.name
assign og_description = page_description | default: shop.description | strip_html | truncate: 200
assign og_url = canonical_url | default: request.origin | append: request.path
assign og_type = 'website'
assign og_image = ''
-%}
{%- if template.name == 'product' -%}
{%- assign og_type = 'product' -%}
{%- if product.featured_image -%}
{%- assign og_image = product.featured_image | image_url: width: 1200, height: 630 -%}
{%- endif -%}
{%- elsif template.name == 'article' -%}
{%- assign og_type = 'article' -%}
{%- if article.image -%}
{%- assign og_image = article.image | image_url: width: 1200, height: 630 -%}
{%- endif -%}
{%- elsif template.name == 'collection' -%}
{%- if collection.image -%}
{%- assign og_image = collection.image | image_url: width: 1200, height: 630 -%}
{%- endif -%}
{%- endif -%}
{%- if og_image == blank and shop.brand.cover_image -%}
{%- assign og_image = shop.brand.cover_image | image_url: width: 1200, height: 630 -%}
{%- endif -%}
<!-- Open Graph -->
<meta property="og:site_name" content="{{ shop.name }}">
<meta property="og:title" content="{{ og_title }}">
<meta property="og:description" content="{{ og_description }}">
<meta property="og:url" content="{{ og_url }}">
<meta property="og:type" content="{{ og_type }}">
{%- if og_image != blank -%}
<meta property="og:image" content="{{ og_image }}">
<meta property="og:image:width" content="1200">
<meta property="og:image:height" content="630">
{%- endif -%}
{%- if template.name == 'product' -%}
<meta property="product:price:amount" content="{{ product.price | money_without_currency }}">
<meta property="product:price:currency" content="{{ cart.currency.iso_code }}">
{%- endif -%}
<!-- Twitter Card -->
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{ og_title }}">
<meta name="twitter:description" content="{{ og_description }}">
{%- if og_image != blank -%}
<meta name="twitter:image" content="{{ og_image }}">
{%- endif -%}
Includilo in theme.liquid con {% render 'social-meta-tags' %} dentro <head>. Le dimensioni immagine 1200x630 sono ottimizzate sia per Facebook sia per il formato large card di Twitter/X.
Se vendi in più paesi o lingue, i tag hreflang dicono a Google quale versione di una pagina mostrare a quale pubblico. Senza, il tuo store francese può apparire nei risultati di ricerca tedeschi — e sì, ho visto succedere esattamente questo a un cliente che riceveva ticket di supporto nella lingua sbagliata per mesi prima di capire la causa.
Shopify Markets genera automaticamente i tag hreflang per domini internazionali e sottocartelle. Ma se ti serve un controllo personalizzato — o stai usando un’app di traduzione — aggiungi questo alla sezione <head> di theme.liquid:
{%- if request.locale and localization.available_languages.size > 1 -%}
{%- for locale in localization.available_languages -%}
{%- if locale.primary -%}
{%- assign locale_root = shop.url -%}
{%- else -%}
{%- assign locale_root = shop.url | append: '/' | append: locale.iso_code -%}
{%- endif -%}
<link rel="alternate"
hreflang="{{ locale.iso_code }}"
href="{{ locale_root }}{{ request.path }}">
{%- endfor -%}
{%- comment -%} x-default points to your primary language {%- endcomment -%}
<link rel="alternate"
hreflang="x-default"
href="{{ shop.url }}{{ request.path }}">
{%- endif -%}
Il tag x-default è fondamentale — dice a Google quale versione mostrare quando nessuna delle lingue specificate corrisponde alla preferenza dell’utente. Puntalo sempre al tuo mercato principale.
Il lazy loading nativo è ora supportato dal 92% dei browser. Il filtro Liquid image_tag di Shopify lo gestisce automaticamente — le immagini oltre le prime tre sezioni ricevono loading="lazy" di default.
Ma devi assicurarti di non applicare il lazy loading alle immagini above the fold, che è un errore comune e in realtà peggiora l’LCP. Il mese scorso ho analizzato uno store che aveva loading="lazy" sul banner hero — stava aggiungendo 800ms al suo LCP perché il browser de-prioritizzava l’immagine più importante della pagina:
{%- comment -%}
Hero image — above the fold, NEVER lazy load.
Use loading: 'eager' and fetchpriority: 'high' to prioritize it.
{%- endcomment -%}
{{ section.settings.hero_image
| image_url: width: 1920
| image_tag:
loading: 'eager',
fetchpriority: 'high',
sizes: '100vw',
widths: '375, 750, 1100, 1500, 1920'
}}
{%- comment -%}
Product images below the fold — let Shopify's default lazy loading work.
Don't set loading attribute; Shopify handles it via section.index.
{%- endcomment -%}
{{ product.featured_image
| image_url: width: 800
| image_tag:
sizes: '(min-width: 750px) 50vw, 100vw',
widths: '375, 750, 800'
}}
L’attributo fetchpriority: 'high' sulla tua immagine LCP è un’aggiunta più recente che dice al browser di dare priorità a questa immagine rispetto ad altre risorse. Insieme al fatto di non caricarla in lazy, è probabilmente la singola modifica più impattante che puoi fare per il tuo punteggio LCP.

Hai fatto le modifiche. Ora verifica che funzionino davvero. (Ho visto sviluppatori saltare questo passaggio e scoprire tre mesi dopo che il loro schema era invalido per tutto il tempo.)
Step 1: valida i dati strutturati. Vai su Google's Rich Results Test e inserisci la URL del prodotto. Ogni tipo di schema che hai aggiunto dovrebbe apparire con un segno di spunta verde. Errori comuni: virgole mancanti negli array JSON-LD, virgolette non escape nelle descrizioni prodotto e URL immagine non valide.
Step 2: controlla i canonical tag. Visualizza il sorgente della pagina (Ctrl+U) e cerca “canonical”. Dovresti vedere esattamente un canonical tag per pagina. Se ne vedi due, il tuo tema ne stava già aggiungendo uno — rimuovi il duplicato.
Step 3: esegui Lighthouse. Apri Chrome DevTools → Lighthouse → seleziona Performance e SEO → Generate Report. Confronta i punteggi prima e dopo. Tieni gli screenshot — ti serviranno quando un cliente ti chiederà cosa hai fatto esattamente.
Step 4: testa le anteprime social. Usa Facebook's Sharing Debugger e Twitter's Card Validator per vedere come appaiono le tue pagine prodotto quando vengono condivise.
Errori comuni che ti si ritorceranno contro:

Ecco cosa puoi aspettarti realisticamente da ogni ottimizzazione, sulla base degli audit che ho eseguito su store basati su Dawn:
| Ottimizzazione | Variazione LCP attesa | Variazione CLS attesa | Punti Lighthouse |
|---|---|---|---|
| Preload dei font | -300ms to -1.2s | Nessuna variazione | +3 to +8 |
| Inlining del CSS critico | -200ms to -800ms | -0.02 to -0.05 | +5 to +15 |
| Defer del JS non critico | -100ms to -500ms | Nessuna variazione | +3 to +10 |
| Lazy loading immagini (corretto) | -100ms to -400ms | Leggero miglioramento | +2 to +5 |
| Schema markup (tutti i tipi) | Nessuna variazione | Nessuna variazione | +2 to +5 (punteggio SEO) |
| Canonical + hreflang | Nessuna variazione | Nessuna variazione | +1 to +3 (punteggio SEO) |
| Totale combinato | -0.7s to -2.9s | -0.02 to -0.05 | +15 to +35 |
Questi numeri variano parecchio in base al punto di partenza. Uno store che è già a 85+ su Lighthouse vedrà guadagni più piccoli rispetto a uno che parte da 55. Il preload dei font e il CSS critico sono le due modifiche che producono più spesso i miglioramenti maggiori — se hai poco tempo, inizia da quelle.
Se stai modificando direttamente il codice del tema — no. Gli aggiornamenti del tema sovrascriveranno le tue modifiche. Per questo consiglio di usare snippet (json-ld-product.liquid, social-meta-tags.liquid, ecc.) quando possibile. Gli snippet sopravvivono ad alcuni aggiornamenti. Per la massima sicurezza, documenta ogni modifica che fai e riapplicala dopo gli update, oppure usa un workflow con version control tramite Shopify CLI.
Sì, app come JSON-LD for SEO e Smart SEO gestiscono lo schema markup senza modifiche al codice. Il compromesso: le app aggiungono overhead JavaScript (ironico, per un’ottimizzazione performance), costano $5-20/mese e ti lasciano meno controllo. Per preload dei font e CSS critico, non c’è un vero sostituto alle modifiche a livello di codice.
Lo schema markup, i tag OG e il codice hreflang funzionano con qualsiasi tema Shopify. La sintassi del preload dei font può variare — controlla il settings_schema.json del tuo tema per i nomi corretti delle variabili font. Il CSS critico, per definizione, dipende dal tema. I pattern di codice Liquid invece sono universali nei temi Online Store 2.0.
Usa Google's Rich Results Test (search.google.com/test/rich-results) per le singole URL. Per una validazione site-wide, controlla Google Search Console → Enhancements. Google può impiegare 2-4 settimane per elaborare nuovi dati strutturati e mostrare i risultati nel report Enhancements.
defer e async nei tag script?defer scarica lo script in parallelo ma aspetta a eseguirlo finché l’HTML non è stato completamente analizzato. async scarica in parallelo ed esegue subito appena è pronto — il che può interrompere il parsing dell’HTML. Per gli script del tema, usa sempre defer. Usa async solo per script davvero indipendenti, come analytics, che non interagiscono con il DOM.
Queste 10 ottimizzazioni coprono i cambiamenti a più alto impatto che puoi fare su theme.liquid. Ma la SEO è più del codice. Le descrizioni prodotto, il linking interno, gli alt text delle immagini e la strategia contenuti contano almeno quanto.
Se vuoi approfondire:
no credit card required