TL;DR
Le fichier theme.liquid de Shopify contrôle la section <head> de chaque page — donc, en pratique, il contrôle la façon dont les moteurs de recherche voient toute ta boutique. J’ai compilé 10 optimisations capables de faire passer un thème Dawn d’un score Lighthouse de 65 à 90+. Pour chacune, tu as du code Liquid à copier-coller, une explication de ce que ça fait, et les pièges à éviter. Temps total d’implémentation : 2-4 heures.
J’ai audité des centaines de boutiques Shopify via SEOJuice. Le schéma est toujours le même : le propriétaire choisit un thème, personnalise les couleurs, met en ligne les produits, puis ne touche plus jamais à theme.liquid. Pendant ce temps, son concurrent — avec les mêmes produits et deux fois moins de backlinks — le dépasse dans Google simplement parce que son développeur a passé un après-midi à optimiser le code du thème.
Le thème Shopify Dawn obtient 92 sur PageSpeed par défaut — c’est le thème gratuit Shopify le plus rapide disponible. Mais même tel quel, il reste encore des gains SEO importants à aller chercher. Données structurées Schema.org manquantes. Chargement des polices non optimisé. Aucun preconnect. Balises OG incomplètes.
Ce guide corrige tout ça. Et la section suivante — les balises canoniques — c’est vraiment celle que j’aimerais que chaque propriétaire de boutique Shopify comprenne avant de venir me demander pourquoi Google indexe la mauvaise version de ses pages produit.
Si tu n’es pas développeur, voici la version en 30 secondes : theme.liquid est le fichier de template maître qui enveloppe absolument chaque page de ta boutique Shopify. Il définit la structure <html>, <head> et <body>. Chaque page produit, page collection, article de blog et page d’accueil hérite de ce fichier.
Quand tu ajoutes quelque chose dans la section <head> de theme.liquid, ça apparaît sur toutes les pages. C’est donc l’endroit idéal pour :
Pour le modifier : Admin Shopify → Boutique en ligne → Thèmes → Modifier le code → Layout → theme.liquid
À retenir
Duplique toujours ton thème avant de le modifier. Va dans Boutique en ligne → Thèmes → Actions → Dupliquer. Travaille sur la copie. Si quelque chose ne fonctionne plus, tu peux revenir en arrière instantanément.
Voici ce qu’on va implémenter, où l’ajouter, et ce que ça t’apporte :
| Optimisation | Où l’ajouter | Impact SEO | Difficulté |
|---|---|---|---|
| Balises canoniques | theme.liquid <head> | Élevé — évite le contenu dupliqué | Facile |
| Schéma Organization | theme.liquid <head> | Moyen — knowledge panel de marque | Facile |
| Schéma Product | product.liquid ou section | Élevé — résultats enrichis dans les SERP | Moyen |
| Schéma BreadcrumbList | Snippet + template | Moyen — fil d’Ariane dans les SERP | Moyen |
| Préchargement des polices | theme.liquid <head> | Élevé — amélioration du LCP | Facile |
| Intégration du CSS critique | theme.liquid <head> | Élevé — élimine le CSS bloquant le rendu | Difficile |
| Différer le JS non critique | theme.liquid fin du <body> | Élevé — parsing du DOM plus rapide | Moyen |
| Balises meta OG/Twitter | theme.liquid <head> | Moyen — partage social | Facile |
| Balises hreflang | theme.liquid <head> | Élevé — SEO international | Moyen |
| Chargement différé des images | Templates/sections | Moyen — réduit le poids au chargement initial | Facile |
Shopify génère énormément d’URL dupliquées. Un produit peut être accessible via /products/widget et /collections/sale/products/widget — même produit, deux URL. Sans balise canonique, Google voit du contenu dupliqué et peut indexer la mauvaise version. J’ai vu des boutiques où Google indexait l’URL avec le chemin de collection pour absolument tous les produits, ce qui signifiait que les “vraies” URL produit n’avaient quasiment aucune autorité.
Dawn inclut une gestion canonique de base, mais ça mérite d’être vérifié et, au besoin, renforcé. Ajoute ceci dans la section <head> de 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 -%}
Ce code fait trois choses : il définit l’URL canonique en utilisant la variable native canonical_url de Shopify (qui résout la bonne URL pour les produits, peu importe la façon dont ils sont consultés), il ajoute rel="prev"/rel="next" pour les collections paginées, et il prévoit un fallback vers l’origine de la requête + le chemin si canonical_url n’est pas disponible.
À retenir
Vérifie que ton thème n’insère pas déjà une balise canonique avant d’ajouter ça. Des balises canoniques en double embrouillent davantage les moteurs de recherche que l’absence totale de balise. Cherche d’abord “canonical” dans ton fichier theme.liquid.
Le schéma Organization indique à Google qui tu es — nom de l’entreprise, logo, profils sociaux et informations de contact. C’est ce qui alimente le knowledge panel qui apparaît quand quelqu’un cherche ta marque. J’ai regardé des boutiques passer d’aucun knowledge panel à une présence de marque bien renseignée en moins de trois semaines après avoir ajouté ce schéma correctement.
Ajoute ceci dans le <head> de theme.liquid. Ce code est chargé sur chaque page, ce qui est exactement ce que Google veut :
<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 }}
{%- if shop.address.country != blank -%}
,"address": {
"@type": "PostalAddress",
"addressCountry": {{ shop.address.country | json }}
}
{%- endif -%}
{%- if shop.brand.social_links.size > 0 -%}
,"sameAs": [
{%- for link in shop.brand.social_links -%}
{{ link | json }}{%- unless forloop.last -%},{%- endunless -%}
{%- endfor -%}
]
{%- endif -%}
}
</script>
Tu remarqueras que j’utilise le filtre | json au lieu de | escape. C’est important — json échappe correctement les chaînes pour le contexte JSON-LD, y compris en les entourant de guillemets. Utiliser escape dans du JSON-LD est une erreur classique qui produit un balisage invalide. J’ai débogué ce problème précis sur au moins une douzaine de boutiques clientes — le balisage se valide dans l’outil de test de Google mais échoue silencieusement en production parce que l’échappement est mauvais.
"JSON-LD is Google's preferred format for structured data. If you're implementing schema markup on Shopify, always choose JSON-LD over microdata — it's cleaner, easier to debug, and doesn't mix with your presentation HTML."
Le schéma Product, c’est ce qui te permet d’obtenir ces résultats enrichis dans Google — étoiles, prix, disponibilité. Dawn inclut un schéma produit basique, mais il est souvent incomplet. Voici une version plus complète, que j’ai affinée à force de tests sur des dizaines de boutiques.
Crée un nouveau 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 -%}
Ensuite, inclus-le dans ton template produit (généralement sections/main-product.liquid ou templates/product.liquid) :
{% render 'json-ld-product' %}
Détail important : j’utilise AggregateOffer au lieu d’objets Offer individuels parce que c’est plus propre pour les produits avec plusieurs variantes. Les champs de note utilisent les metafields natifs de Shopify pour les avis produit — si tu utilises une app d’avis tierce, il faudra ajuster le namespace des metafields. (Judge.me et Loox utilisent des namespaces différents — vérifie leur documentation avant de copier-coller ça tel quel.)
Les fils d’Ariane aident Google à comprendre la hiérarchie de ton site et peuvent apparaître directement dans les résultats de recherche à la place de l’URL brute. C’est particulièrement utile pour les boutiques avec des structures de catégories profondes. Sur une boutique que j’ai auditée, l’ajout du schéma BreadcrumbList a transformé l’affichage SERP de example.com › collections › summer-2025 › products › red-dress en un propre Accueil > Collection été > Robe rouge — ce qui a amélioré le taux de clic de manière mesurable.
Crée 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 -%}
Ajoute {% render 'json-ld-breadcrumbs' %} dans ton theme.liquid juste avant </head>. Le snippet détecte automatiquement le type de page et construit la bonne chaîne de fil d’Ariane.
Le prochain point est celui qui a fait la plus grosse différence sur nos boutiques de test. Le préchargement des polices à lui seul peut faire gagner une seconde complète sur le temps d’affichage du texte — et en e-commerce, cette seconde est directement corrélée au taux de rebond. L’équipe d’ingénierie de Shopify a d’ailleurs montré l’impact sur shopify.com lui-même :
"With preload, front-end developers can tell the browser to download the font files needed at the same time as the CSS files. Only 17 lines of code decreased the time to display text on shopify.com by 50%."
Le problème : les navigateurs ne peuvent pas télécharger les polices avant d’avoir fait le parsing du HTML et du CSS qui les référence. Ça fait deux allers-retours séquentiels avant que le texte apparaisse. Le preload dit au navigateur : “commence à télécharger cette police maintenant, tu vas en avoir besoin très vite.”
Ajoute ceci près du haut du <head> dans theme.liquid, avant tes balises de stylesheet :
{%- 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’attribut crossorigin est obligatoire pour le preload des polices — sans lui, le navigateur télécharge la police deux fois. J’ai vu cette erreur exacte sur des boutiques en production, où le simple fait de supprimer le téléchargement en double a amélioré le LCP de 400ms. Les balises preconnect font gagner environ ~100ms supplémentaires en établissant tôt la connexion TCP + TLS vers le CDN de Shopify.
À retenir
Ne précharge que les polices utilisées au-dessus de la ligne de flottaison. Chaque preload entre en concurrence pour la bande passante. Si tu précharges cinq polices, tu annules complètement l’intérêt de l’optimisation — le navigateur va télécharger les cinq en parallèle et ralentir tout le reste.
Le CSS qui bloque le rendu est la raison n°1 pour laquelle les boutiques Shopify obtiennent de mauvais scores sur Lighthouse. Le navigateur ne peut rien afficher tant qu’il n’a pas téléchargé et analysé toute ta feuille de style — même le CSS du footer situé 3,000px sous la ligne de flottaison. C’est l’optimisation la plus difficile de cette liste, mais c’est aussi celle qui apporte le plus gros gain Lighthouse dans la majorité des audits que j’ai réalisés.
La solution : intégrer le CSS nécessaire au contenu above the fold directement dans le <head>, puis charger le reste de manière asynchrone.
{%- 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>
Autre option : Shopify fournit le filtre Liquid inline_asset_content, qui permet d’intégrer directement le CSS depuis tes fichiers d’assets, sans snippet séparé :
<style>{{ 'critical.css' | asset_url | inline_asset_content }}</style>
Générer correctement le CSS critique demande d’analyser chaque template. Je te recommande d’utiliser un outil de génération de CSS critique — il extrait exactement les règles CSS nécessaires au contenu above the fold pour chaque type de page. Si tu te rates ici (par exemple en oubliant une règle pour ta hero section), tu provoques un flash de contenu non stylé, ce qui est pire que de ne rien optimiser du tout.
Les scripts qui bloquent le parser sont le deuxième plus gros tueur de performance après le CSS bloquant. Les recommandations officielles de Shopify sont claires : ton bundle JS minifié devrait faire 16KB ou moins, et tout le reste devrait être différé.
Dans theme.liquid, trouve tes balises <script> et ajoute 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>
Le pattern de chargement basé sur l’interaction est agressif, mais efficace. Les widgets de chat, popups d’avis et fonctionnalités similaires n’ont pas besoin de se charger tant que l’utilisateur n’a rien fait sur la page. Sur une boutique que nous avons testée, ce pattern a fait gagner 340ms sur le Time to Interactive — ce qui a suffi à passer d’un score INP “needs improvement” à “good” dans les données CrUX.
Quand quelqu’un partage ton produit sur Facebook, Twitter ou LinkedIn, les balises OG (Open Graph) contrôlent ce qui apparaît dans l’aperçu. Sans elles, les plateformes sociales improvisent — et elles improvisent très mal. J’ai déjà vu des partages produit afficher le favicon de la boutique comme image et le titre de la page checkout comme description.
Dawn inclut un snippet social-meta-tags.liquid, mais il est souvent minimaliste. Voici une version plus complète. Crée ou mets à jour 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 -%}
Inclus-le dans theme.liquid avec {% render 'social-meta-tags' %} à l’intérieur du <head>. Les dimensions d’image 1200x630 sont optimisées à la fois pour Facebook et pour le format large card de Twitter/X.
Si tu vends dans plusieurs pays ou langues, les balises hreflang indiquent à Google quelle version d’une page montrer à quelle audience. Sans elles, ta boutique française peut apparaître dans les résultats allemands — et oui, j’ai déjà vu exactement ça chez un client qui recevait des tickets support dans la mauvaise langue pendant des mois avant de comprendre la cause.
Shopify Markets génère automatiquement les balises hreflang pour les domaines internationaux et les sous-dossiers. Mais si tu as besoin d’un contrôle plus fin — ou si tu utilises une app de traduction — ajoute ceci dans le <head> de 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 -%}
La balise x-default est cruciale — elle indique à Google quelle version afficher quand aucune des langues spécifiées ne correspond à la préférence de l’utilisateur. Fais-la toujours pointer vers ton marché principal.
Le chargement différé natif est maintenant pris en charge par 92% des navigateurs. Le filtre Liquid image_tag de Shopify gère ça automatiquement — les images après les trois premières sections reçoivent loading="lazy" par défaut.
Mais tu dois t’assurer de ne pas appliquer le chargement différé aux images above the fold, ce qui est une erreur fréquente qui nuit en réalité au LCP. J’ai audité une boutique le mois dernier qui avait loading="lazy" sur sa bannière hero — ça ajoutait 800ms à son LCP parce que le navigateur dépriorisait l’image la plus importante de la page :
{%- 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’attribut fetchpriority: 'high' sur ton image LCP est un ajout plus récent qui indique au navigateur de prioriser cette image par rapport aux autres ressources. Combiné au fait de ne pas la charger en lazy, c’est probablement l’action la plus impactante que tu puisses faire pour ton score LCP.

Tu as fait les changements. Maintenant, vérifie qu’ils fonctionnent vraiment. (J’ai déjà vu des développeurs sauter cette étape et découvrir trois mois plus tard que leurs données structurées Schema.org étaient invalides depuis le début.)
Étape 1 : valide les données structurées. Va sur le Rich Results Test de Google et entre l’URL de ton produit. Chaque type de schéma que tu as ajouté devrait apparaître avec une coche verte. Erreurs fréquentes : virgules manquantes dans les tableaux JSON-LD, guillemets non échappés dans les descriptions produit, et URL d’image invalides.
Étape 2 : vérifie les balises canoniques. Affiche le code source de la page (Ctrl+U) et cherche “canonical”. Tu dois voir exactement une seule balise canonique par page. Si tu en vois deux, ton thème en ajoutait déjà une — supprime le doublon.
Étape 3 : lance Lighthouse. Ouvre Chrome DevTools → Lighthouse → coche Performance et SEO → Generate Report. Compare tes scores avant/après. Garde des captures d’écran — tu seras content de les avoir quand un client te demandera ce que tu as fait concrètement.
Étape 4 : teste les aperçus sociaux. Utilise le Sharing Debugger de Facebook et le Card Validator de Twitter pour prévisualiser l’apparence de tes pages produit lors d’un partage.
Erreurs fréquentes qui vont te retomber dessus :

Voici ce que tu peux raisonnablement attendre de chaque optimisation, d’après les audits que j’ai menés sur des boutiques basées sur Dawn :
| Optimisation | Variation attendue du LCP | Variation attendue du CLS | Points Lighthouse |
|---|---|---|---|
| Préchargement des polices | -300ms à -1.2s | Aucun changement | +3 à +8 |
| Intégration du CSS critique | -200ms à -800ms | -0.02 à -0.05 | +5 à +15 |
| Différer le JS non critique | -100ms à -500ms | Aucun changement | +3 à +10 |
| Chargement différé des images (correct) | -100ms à -400ms | Légère amélioration | +2 à +5 |
| Données structurées Schema.org (tous types) | Aucun changement | Aucun changement | +2 à +5 (score SEO) |
| Canonical + hreflang | Aucun changement | Aucun changement | +1 à +3 (score SEO) |
| Total combiné | -0.7s à -2.9s | -0.02 à -0.05 | +15 à +35 |
Ces chiffres varient beaucoup selon ton point de départ. Une boutique déjà à 85+ sur Lighthouse verra des gains plus modestes qu’une boutique à 55. Le préchargement des polices et le CSS critique apportent de façon constante les plus grosses améliorations — si tu manques de temps, commence par ces deux-là.
Si tu modifies directement le code du thème — non. Les mises à jour du thème écraseront tes changements. C’est pour ça que je recommande d’utiliser des snippets (json-ld-product.liquid, social-meta-tags.liquid, etc.) dès que possible. Les snippets survivent à certaines mises à jour. Pour être vraiment tranquille, documente chaque changement que tu fais et réapplique-les après les updates, ou utilise un workflow de versioning avec Shopify CLI.
Oui, des apps comme JSON-LD for SEO et Smart SEO gèrent les données structurées Schema.org sans modification de code. Le compromis : les apps ajoutent une surcharge JavaScript (ironique pour une optimisation de performance), coûtent $5-20/mois, et te donnent moins de contrôle. Pour le préchargement des polices et le CSS critique, rien ne remplace des changements au niveau du code.
Les données structurées Schema.org, les balises OG et le code hreflang fonctionnent avec n’importe quel thème Shopify. La syntaxe de préchargement des polices peut varier — vérifie le settings_schema.json de ton thème pour trouver les bons noms de variables de police. Le CSS critique est, par définition, spécifique au thème. Les patterns de code Liquid restent universels sur les thèmes Online Store 2.0.
Utilise le Rich Results Test de Google (search.google.com/test/rich-results) pour des URL individuelles. Pour une validation à l’échelle du site, regarde Google Search Console → Améliorations. Google peut mettre 2-4 semaines à traiter de nouvelles données structurées et à afficher les résultats dans le rapport Améliorations.
defer et async sur les balises script ?defer télécharge le script en parallèle mais attend que le HTML soit entièrement analysé avant de l’exécuter. async le télécharge en parallèle et l’exécute dès qu’il est prêt — ce qui peut interrompre l’analyse du HTML. Pour les scripts du thème, utilise toujours defer. N’utilise async que pour des scripts vraiment indépendants, comme l’analytics, qui n’interagissent pas avec le DOM.
Ces 10 optimisations couvrent les changements les plus impactants que tu peux faire dans theme.liquid. Mais le SEO, c’est plus que du code. Tes descriptions produit, ton maillage interne, les textes alt de tes images et ta stratégie de contenu comptent tout autant.
Si tu veux aller plus loin :
no credit card required
No related articles found.