Publié le 7 février 2026 SEO Technique

SEO technique : Sitemap index, index de sitemap et fichier XML

Introduction

Le SEO technique (Search Engine Optimization) est un levier essentiel pour améliorer la visibilité d'un site web dans les résultats des moteurs de recherche. Parmi les nombreuxéléments techniques à maîtriser figure le sitemap XML et, pour les sites volumineux, le sitemap index (ou index de sitemap). Ce fichier XML spécifique joue un rôle clé dans la découverte et le crawling des URLs importantes d’un site, en particulier lorsque celui-ci comporte des dizaines de milliers de pages.

Cet article approfondit le concept de sitemap index SEO, ses composantes, ses limites techniques, les bonnes pratiques pour l’utiliser au mieux, ainsi que les principaux outils pour le générer, le contrôler et l’optimiser.

Concepts clés

Qu’est-ce qu’un sitemap XML ?

Un sitemap XML est un fichier au format XML qui liste les URLs importantes d’un site web afin d’aider les moteurs de recherche à les découvrir et à les explorer plus efficacement. Il peut contenir, selon les règles en vigueur :

  • jusqu’à 50 000 URLs par fichier ;
  • une taille maximale de 50 Mo non compressés.

Lorsqu’un site dépasse l’une de ces limites, il doitêtre découpé en plusieurs fichiers de sitemap XML. C’est précisément dans ce contexte qu’intervient le fichier d’index de sitemap.

Qu’est-ce qu’un sitemap index ?

Un sitemap index (ou index de sitemap) est un fichier XML qui référence plusieurs fichiers de sitemap individuels. Il joue le rôle de table des matières pour les sitemaps d’un site. Au lieu de soumettre chaque sitemap séparément, on soumet un seul fichier d’index qui liste tous les sitemaps à explorer.

Ce type de fichier est particulièrement adapté aux sites web volumineux ou complexes, par exemple :

  • grands sites e‑commerce avec des dizaines de milliers de fiches produits ;
  • sites média ou d’actualité avec un volume de contenu publié quotidiennement ;
  • plateformes multi‑langues ou multi‑pays ;
  • sites à forte composante UGC (contenus générés par les utilisateurs).

D’un point de vue structurel, un index de sitemap :

  • estécrit en XML ;
  • utilise la balise racine ;
  • contient une série de balises , chacune pointant vers un fichier de sitemap via une balise ;
  • peut, en option, inclure une balise pour indiquer la dernière date de mise à jour de chaque sitemap.

Un fichier d’index de sitemap peut lister jusqu’à 50 000 fichiers de sitemap et est lui aussi limité à 50 Mo non compressés.

Exemple concret d’utilisation d’un sitemap index

Imaginons un site e‑commerce comportant plusieurs centaines de milliers de produits, des pages de catégories, des pages de contenuséditoriaux et un blog. Un découpage efficace du sitemap index pourraitêtre :

  • un sitemap index principal : /sitemap_index.xml ;
  • un sitemap pour les pages de catégories produits : /sitemap-categories.xml ;
  • plusieurs sitemaps pour les fiches produits, segmentés par catégorie ou ID produit (par exemple /sitemap-products-1.xml, /sitemap-products-2.xml, etc.) ;
  • un sitemap pour les pages statiques (conditions générales, contact, à propos, etc.) ;
  • un sitemap pour les articles de blog ;
  • le caséchéant, un sitemap d’images ou de vidéos.

Le fichier sitemap_index.xml référence tous ces sitemaps, ce qui permet aux moteurs de recherche de les découvrir à partir d’un seul point d’entrée.

Fonctionnement du sitemap index

Lorsqu’un moteur de recherche comme Google ou Bing accède à un site doté d’un sitemap index XML, il commence par analyser ce fichier central afin de récupérer la liste de tous les fichiers de sitemap à explorer. Pour chaque sitemap référencé, le robot va ensuite :

  • télécharger le fichier de sitemap ;
  • parcourir les URLs listées ;
  • programmer ou ajuster le crawl des URLs jugées pertinentes.

Cette approche permet :

  • d’optimiser le processus de crawling, en indiquant explicitement quelles URLs sont importantes et doiventêtre découvertes en priorité ;
  • d’accélérer la découverte de nouveaux contenus ou de contenus mis à jour, surtout sur les sites de grande taille ;
  • de segmenter le suivi de l’indexation dans les outils comme Google Search Console, en contrôlant fichier par fichier les taux de couverture, les erreurs et les exclusions.

Il est important de rappeler que la présence d’une URL dans un sitemap (ou un sitemap index) ne garantit pas son indexation. Le sitemap est un signal de découverte et de priorité, mais les moteurs de recherche restent libres de décider quelles pages seront réellement indexées.

Avantages du sitemap index

  • Amélioration du crawling : en agrégeant les sitemaps, l’index permet aux robots d’identifier rapidement tous les ensembles d’URLs importants à explorer.
  • Gestion simplifiée des très grands sites : un seul fichier de soumission regroupe jusqu’à 50 000 sitemaps, eux‑mêmes pouvant contenir chacun jusqu’à 50 000 URLs.
  • Suivi granulaire dans Google Search Console : en segmentant les contenus par type (produits, catégories, blog, international, etc.), il devient plus simple de repérer où se concentrent les problèmes d’indexation.
  • Flexibilité pour les sites dynamiques : les sitemaps peuventêtre générés automatiquement et mis à jour en temps réel ou quasi temps réel, l’index se contentant de les référencer.
  • Meilleure organisation thématique : en regroupant les URLs par thématique, langue ou section, on facilite la compréhension de l’architecture du site par les moteurs de recherche.

Limites et contraintes techniques à connaître

Limites de taille et de nombre

Pour construire un sitemap index conforme aux règles actuelles, il est indispensable de respecter les limites suivantes :

  • Par sitemap XML :
    • jusqu’à 50 000 URLs par fichier ;
    • taille maximale de 50 Mo (non compressés).
  • Par fichier d’index de sitemap :
    • jusqu’à 50 000 sitemaps référencés (soit jusqu’à 50 000 balises ) ;
    • taille maximale de 50 Mo non compressés.

Si un site dépasse ces seuils, il est possible d’utiliser une structure de sitemap index imbriquée, généralement supportée jusqu’à deux niveaux d’index (un index pouvant référencer d’autres index qui référencent à leur tour les sitemaps finaux).

Emplacement et cohérence des URLs

Quelques règles importantes doiventêtre respectées pour que les moteurs de recherche puissent exploiter correctement un sitemap index :

  • les sitemaps listés dans un sitemap index doivent normalementêtre hébergés sur le même site ou au sein de répertoires cohérents (sauf cas particuliers avec mécanismes de soumission croisée avancés) ;
  • un sitemap ou un sitemap index affecte en priorité les URLs situées au même niveau ou en dessous dans l’arborescence du site (répertoires descendants) ;
  • les URLs indiquées dans les sitemaps doivent utiliser des URLs absolues (avec schéma et nom de domaine complet).

Types de sitemaps pris en charge

Un sitemap index peut référencer différents types de sitemaps, selon la nature du contenu :

  • Sitemap XML standard : pour les pages HTML classiques d’un site (produits, catégories, pages de contenu, articles de blog, etc.).
  • Image sitemap : pour référencer des images via des balises spécifiques, utile pour les sites très visuels (e‑commerce, banque d’images, etc.).
  • Video sitemap : pour fournir des métadonnées avancées sur des contenus vidéo.
  • News sitemap : pour les sites d’actualités. Ce type de sitemap doit contenir uniquement les contenus publiés durant les deux derniers jours. Au-del à de cette fenêtre temporelle, il est recommandé de retirer les URLs du sitemap d’actualités ou, a minima, d’enlever la balise spécifique d’actualité.

Bonnes pratiques pour les sitemaps et l’index de sitemap

Inclure uniquement des URLs indexables

Pour maximiser l’efficacité d’un sitemap index etéviter de gaspiller le crawl budget, il est recommandé de n’y référencer que des URLs potentiellement indexables, c’est‑à‑dire :

  • retournant un code HTTP 200 (pages accessibles sans erreur) ;
  • non bloquées par le fichier robots.txt ;
  • ne comportant pas de balise noindex ;
  • correspondant à l’URL canonique de la page (éviter les paramètres ou variantes dont la canonique pointe ailleurs).

À l’inverse, il est déconseillé d’inclure dans les sitemaps :

  • des URLs 3xx (redirections), 4xx (erreurs côté client) ou 5xx (erreurs serveur) ;
  • des pages explicitement noindex ;
  • des contenus dupliqués ou des variantes que l’on ne souhaite pas indexer ;
  • des URLs bloquées par le robots.txt.

Mettre à jour régulièrement les sitemaps

Sur un site vivant, les sitemaps et le sitemap index doiventêtre mis à jour en continu ou du moins très régulièrement, notamment :

  • ajout des nouvelles pages publiées (nouveaux produits, nouveaux articles, nouvelles pages de contenu) ;
  • mise à jour ou retrait des URLs supprimées ou redirigées ;
  • mise à jour de la balise lorsque des pages sont substantiellement modifiées.

Pour les très grands sites, il est fortement recommandé de recourir à des sitemaps dynamiques, générés automatiquement par le CMS ou par un script, plutôt qu’à une gestion manuelle.

Structurer logiquement l’index de sitemap

Une bonne structuration de l’index de sitemap facilite autant le travail des robots que celui de l’équipe SEO. Quelques approches courantes :

  • Par type de contenu :
    • un ou plusieurs sitemaps pour les produits ;
    • un ou plusieurs sitemaps pour les catégories ;
    • un sitemap pour les pages institutionnelles ;
    • un sitemap pour le blog ;
    • un sitemap pour les images ou les vidéos, si nécessaire.
  • Par date ou volume :
    • segmenter les articles de blog par année ou par mois lorsque le volume est très important ;
    • scinder les fiches produits par tranches d’ID ou de dates d’ajout.
  • Par langue ou pays :
    • un sitemap index par langue (FR, EN, ES, etc.) ;
    • au sein de chaque langue, des sitemaps segmentés par type de page.

Cette organisation thématique rend la structure du site plus claire pour les moteurs de recherche et facilite l’analyse des performances par segment.

Assurer la cohérence avec la structure réelle du site

Le contenu des sitemaps doit refléter fidèlement l’architecture réelle du site. Avant de les soumettre, il est recommandé de vérifier :

  • que toutes les URLs présentes dans les sitemaps sont bien accessibles et renvoient un code 200 ;
  • qu’elles correspondent aux URLs canoniques effectives ;
  • que la hiérarchie implicitement visible via les chemins d’URL (dossiers, segments, paramètres) correspond à la structure souhaitée.

Une incohérence persistante entre la structure réelle du site, les liens internes et les sitemaps peut brouiller les signaux envoyés aux moteurs de recherche et nuire à l’indexation optimale.

Produire des pages de qualité

Un sitemap index efficace ne se limite pas à un simple listing exhaustif d’URLs. Pour en tirer un bénéfice SEO réel, chaque page référencée devrait respecter les fondamentaux de la qualité :

  • un contenu pertinent, unique et utile pour l’utilisateur ;
  • une intention de recherche clairement adressée (informationnelle, transactionnelle, navigationnelle, etc.) ;
  • une optimisation on-page correcte : balises </code> et <code><meta description></code>, titres hiérarchisés (<code>H1</code>, <code>H2</code>, etc.), maillage interne, performance technique raisonnable ;</li> <li>une compatibilité mobile et un chargement rapide.</li> </ul> <p>Les sitemaps et le sitemap index servent avant tout à <strong>mettre en avant les pages les plus importantes</strong>. Si ces pages ne sont pas qualitatives, le simple fait de les notifier aux moteurs de recherche ne suffira pas pour obtenir un bon positionnement.</p> <h2 id="outils-et-ressources-pour-gerer-un-sitemap-index">Outils et ressources pour gérer un sitemap index</h2> <h3 id="outils-essentiels">Outils essentiels</h3> <ul> <li><strong>Google Search Console</strong><br> C’est l’outil principal pour : <ul> <li>soumettre un sitemap ou un sitemap index ;</li> <li>vérifier sa prise en compte ;</li> <li>analyser la couverture des URLs (indexées, exclues, avec erreurs) ;</li> <li>identifier des problèmes de crawl ou d’indexation par fichier de sitemap.</li> </ul> </li> <li><strong>Outils de crawl SEO (ex. : Screaming Frog SEO Spider)</strong><br> Ils permettent de : <ul> <li>générer des sitemaps XML à partir d’un crawl complet du site ;</li> <li>comparer les sitemaps à la réalité du site (URL manquantes, erreurs, incohérences) ;</li> <li>valider les codes HTTP, les directives noindex et le respect des canoniques.</li> </ul> </li> <li><strong>Extensions de CMS (ex. : plugins WordPress comme Yoast SEO ouéquivalents)</strong><br> Ces outils offrent souvent : <ul> <li>une génération automatique des sitemaps XML et de l’index de sitemap ;</li> <li>une mise à jour automatique lors de la publication, la modification ou la suppression de contenus ;</li> <li>des réglages fins pour inclure ou exclure certains types de contenus.</li> </ul> </li> </ul> <h3 id="soumettre-un-sitemap-index-a-google">Soumettre un sitemap index à Google</h3> <p>Pour soumettre un sitemap index à Google, la démarche standard est la suivante :</p> <ul> <li>vérifier que le fichier d’index est accessible publiquement (par exemple <code>https://www.example.com/sitemap_index.xml</code>) ;</li> <li>se connecter à Google Search Console et sélectionner la propriété correspondante au site ;</li> <li>se rendre dans la section dédiée aux sitemaps ;</li> <li>indiquer l’URL du sitemap index (sans le protocole si l’interface le demande déj à) ;</li> <li>cliquer sur « Soumettre » puis attendre que Google l’explore et en affiche le statut.</li> </ul> <p>Une fois le sitemap index pris en compte, Google va progressivement explorer les sitemaps qu’il contient. Les rapports de couverture et les messages d’erreur associés permettront de vérifier la bonne prise en charge du fichier.</p> <h2 id="strategies-avancees-pour-les-gros-sites">Stratégies avancées pour les gros sites</h2> <h3 id="segmentation-par-priorite-et-par-frequence-de-mise-a-jour">Segmentation par priorité et par fréquence de mise à jour</h3> <p>Sur les très gros sites, le sitemap index offre la possibilité de différencier :</p> <ul> <li>les sections mises à jour très fréquemment (actualités, nouveaux produits) ;</li> <li>les sections plus stables (pages d’information, pages institutionnelles) ;</li> <li>les contenus historiquement importants mais peu modifiés (archives, anciens articles).</li> </ul> <p>Cette segmentation permet aux moteurs de recherche de concentrer une partie de leur crawl sur les zones réellement dynamiques du site, ce qui améliore la fraîcheur de l’indexation.</p> <h3 id="sitemaps-imbriques-et-multi-tenant">Sitemaps imbriqués et multi‑tenant</h3> <p>Dans le cas de plateformes multi‑tenant (plusieurs espaces ou sous‑sites hébergés sur la même infrastructure), il est possible d’utiliser une structure d’index de sitemap imbriquée, par exemple :</p> <ul> <li>un sitemap index global listant plusieurs index secondaires ;</li> <li>chaque index secondaire regroupant les sitemaps d’un tenant ou d’une grande section.</li> </ul> <p>Ce type d’architecture reste généralement supporté jusqu’à deux niveaux d’index, sous réserve de respecter les limites de taille et de nombre de sitemaps.</p> <h3 id="controle-de-la-qualite-via-la-comparaison-sitemap-crawl">Contrôle de la qualité via la comparaison sitemap / crawl</h3> <p>Pour les SEO techniques, une approche efficace consiste à comparer régulièrement :</p> <ul> <li>les URLs présentes dans les sitemaps (et donc visibles comme prioritaires pour les moteurs) ;</li> <li>les URLs découvertes par un outil de crawl (Screaming Frog, Sitebulb, etc.).</li> </ul> <p>Lesécarts entre ces deux ensembles d’URLs permettent de détecter :</p> <ul> <li>des pages actives non présentes dans les sitemaps (opportunités manquées) ;</li> <li>des URLs obsolètes, en erreur ou redirigées qui restent listées dans les sitemaps (signaux contradictoires) ;</li> <li>des problèmes de maillage interne, lorsque des pages ne sont accessibles que par sitemap mais très peu par les liens internes.</li> </ul> <h2 id="faq">FAQ</h2> <dl> <dt><strong>Pourquoi faut‑il utiliser un sitemap index ?</strong></dt> <dd>Un sitemap index est particulièrement utile pour les sites volumineux ou complexes. Il permet d’<strong>organiser efficacement plusieurs fichiers de sitemap</strong>, de <strong>faciliter le crawling</strong> des moteurs de recherche et de <strong>segmenter le suivi de l’indexation</strong> dans les outils comme Google Search Console.</dd> <dt><strong>Un sitemap index est‑il obligatoire ?</strong></dt> <dd>Non, un sitemap index n’est pas obligatoire. Pour les petits sites ou les sites de taille moyenne pouvant tenir dans un unique sitemap XML (moins de 50 000 URLs et 50 Mo), un seul fichier sitemap peut suffire. En revanche, il est fortement recommandé dès que la taille ou la complexité du site devient importante.</dd> <dt><strong>Combien d’URLs peut contenir un sitemap et combien de sitemaps peut lister un index ?</strong></dt> <dd>Un sitemap XML peut contenir jusqu’à <strong>50 000 URLs</strong> et peser jusqu’à <strong>50 Mo non compressés</strong>. Un fichier d’index de sitemap peut quant à lui référencer jusqu’à <strong>50 000 sitemaps</strong>, avec la même limite de 50 Mo non compressés pour le fichier d’index.</dd> <dt><strong>Comment soumettre un sitemap index à Google ?</strong></dt> <dd>La soumission se fait via la fonctionnalité « Sitemaps » de Google Search Console. Il suffit d’indiquer l’URL complète du sitemap index, de le soumettre, puis de vérifier son statut et leséventuelles erreurs signalées.</dd> <dt><strong>Les moteurs de recherche indexent‑ils automatiquement toutes les URLs présentes dans le sitemap ?</strong></dt> <dd>Non. Le sitemap et son index servent à <strong>signaler les URLs jugées importantes</strong> et à en faciliter la découverte. Les moteurs de recherche restent libres de ne pas indexer certaines pages, par exemple en cas de faible qualité, de duplication ou de contraintes techniques.</dd> <dt><strong>Peut‑on inclure des URLs noindex ou bloquées par robots.txt dans un sitemap ?</strong></dt> <dd>Il est fortement déconseillé d’inclure dans un sitemap des URLs marquées en <code>noindex</code> ou bloquées par le fichier <code>robots.txt</code>. Cela envoie des signaux contradictoires aux moteurs de recherche et gaspille du budget de crawl.</dd> <dt><strong>Quelle est la différence entre un sitemap XML et un sitemap HTML ?</strong></dt> <dd>Un <strong>sitemap XML</strong> est destiné aux moteurs de recherche : il suit un format normé, lisible par les robots, et peutêtre référencé par un index de sitemap. Un <strong>sitemap HTML</strong> est une page de navigation destinée principalement aux utilisateurs humains, listant les principales sections ou pages d’un site pour en faciliter la navigation.</dd> <dt><strong>Faut‑il créer un sitemap différent pour chaque langue ?</strong></dt> <dd>Pour les sites multilingues ou multi‑pays, il est recommandé de <strong>segmenter les sitemaps par langue ou par pays</strong>. Cette segmentation peut ensuiteêtre gérée par un ou plusieurs indexes de sitemaps, offrant une meilleure visibilité sur l’indexation de chaque version linguistique.</dd> <dt><strong>À quelle fréquence mettre à jour un sitemap index ?</strong></dt> <dd>La fréquence dépend du rythme de mise à jour du site. Sur un site d’actualités ou un grand e‑commerce, une mise à jour automatique et continue est idéale. Sur un site plus statique, une mise à jour hebdomadaire ou mensuelle peut suffire, à condition de l’effectuer dès qu’un volume significatif de nouvelles URLs est créé ou supprimé.</dd> </dl> <p>En maîtrisant les principes techniques du <strong>sitemap index</strong> et en appliquant ces bonnes pratiques, vous pouvez optimiser la découverte et l’indexation de vos pages clés, tout en améliorant la capacité des moteurs de recherche à comprendre la structure et les priorités de votre site.</p> </div> <!-- Articles similaires --> <div class="mt-12 pt-8 border-t border-gray-200"> <h2 class="text-2xl font-bold text-gray-900 mb-6 flex items-center gap-2"> <i class="fas fa-newspaper text-purple-600"></i> Articles similaires </h2> <div class="grid gap-4"> <a href="/blog/seo-technique-sitemap-plan-du-site-et-fichier-xml/" class="block p-4 bg-white rounded-lg border border-gray-200 hover:border-purple-300 hover:shadow-md transition"> <span class="text-purple-600 font-semibold hover:text-purple-800">SEO Technique : Sitemap, Plan du site et Fichier XML</span> </a> <a href="/blog/seo-technique-news-sitemap-sitemap-actualites-et-fichier-xml/" class="block p-4 bg-white rounded-lg border border-gray-200 hover:border-purple-300 hover:shadow-md transition"> <span class="text-purple-600 font-semibold hover:text-purple-800">SEO technique : News Sitemap, Sitemap actualités et fichier XML</span> </a> <a href="/blog/seo-technique-sitemap-xml-fichier-sitemap-xml-et-bonnes-pratiques-avancees/" class="block p-4 bg-white rounded-lg border border-gray-200 hover:border-purple-300 hover:shadow-md transition"> <span class="text-purple-600 font-semibold hover:text-purple-800">SEO Technique : Sitemap XML, Fichier sitemap.xml et Bonnes Pratiques Avancées</span> </a> </div> </div> <!-- CTA Section --> <div class="mt-12 pt-8 border-t border-gray-200"> <div class="bg-gradient-to-r from-purple-600 to-blue-600 rounded-xl p-8 text-center text-white"> <h3 class="text-2xl font-bold mb-4">Besoin d'aide avec votre SEO ?</h3> <p class="mb-6 text-purple-100">Notreéquipe d'experts peut vous aider à optimiser votre site e-commerce</p> <div class="flex flex-col sm:flex-row gap-4 justify-center"> <a href="/seo-ecommerce" class="bg-white text-purple-600 px-8 py-3 rounded-lg font-semibold hover:bg-purple-50 transition inline-block"> Découvrir nos services SEO </a> <a href="/#contact" class="bg-purple-800 text-white px-8 py-3 rounded-lg font-semibold hover:bg-purple-900 transition inline-block"> Nous contacter </a> </div> </div> </div> </article> <!-- Section Commentaires --> <div class="mt-12 max-w-4xl mx-auto bg-white rounded-2xl shadow-xl p-8 md:p-12"> <h2 class="text-3xl font-bold text-gray-900 mb-6 flex items-center gap-3"> <i class="fas fa-comments text-purple-600"></i> Commentaires </h2> <!-- Liste des commentaires approuvés --> <div id="comments-list" class="mb-8 space-y-6"> <!-- Les commentaires approuvés seront chargés ici via JavaScript --> </div> <!-- Formulaire de commentaire --> <div class="border-t border-gray-200 pt-8"> <h3 class="text-xl font-semibold text-gray-900 mb-4">Laisser un commentaire</h3> <form id="comment-form" class="space-y-4"> <input type="hidden" id="article-slug" value="seo-technique-sitemap-index-index-de-sitemap-et-fichier-xml"> <div class="grid md:grid-cols-2 gap-4"> <div> <label for="comment-name" class="block text-sm font-medium text-gray-700 mb-2">Nom *</label> <input type="text" id="comment-name" name="name" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-600 focus:border-transparent"> </div> <div> <label for="comment-email" class="block text-sm font-medium text-gray-700 mb-2">Email *</label> <input type="email" id="comment-email" name="email" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-600 focus:border-transparent"> </div> </div> <div> <label for="comment-message" class="block text-sm font-medium text-gray-700 mb-2">Message *</label> <textarea id="comment-message" name="message" rows="5" required class="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-purple-600 focus:border-transparent"></textarea> </div> <div class="text-sm text-gray-600"> <i class="fas fa-info-circle text-purple-600"></i> Votre commentaire sera soumis à modération avant publication. </div> <button type="submit" class="bg-gradient-to-r from-purple-600 to-blue-600 text-white px-8 py-3 rounded-lg font-semibold hover:from-purple-700 hover:to-blue-700 transition inline-flex items-center gap-2"> <i class="fas fa-paper-plane"></i> Publier le commentaire </button> </form> <div id="comment-status" class="mt-4 hidden"></div> </div> </div> <!-- Navigation Articles --> <div class="mt-12 max-w-4xl mx-auto"> <a href="/blog" class="inline-flex items-center gap-2 text-purple-600 hover:text-purple-800 transition font-semibold"> <i class="fas fa-arrow-left"></i> Retour au blog </a> </div> </div> </section> <!-- Script pour les commentaires --> <script> const articleSlug = 'seo-technique-sitemap-index-index-de-sitemap-et-fichier-xml'; // Charger les commentaires approuvés async function loadComments() { try { const response = await fetch(`/gestion-commentaires/get-comments.php?slug=${articleSlug}`); const data = await response.json(); const commentsList = document.getElementById('comments-list'); if (data.success && data.comments.length > 0) { commentsList.innerHTML = data.comments.map(comment => ` <div class="border-l-4 border-purple-600 pl-4 py-2"> <div class="flex items-center gap-2 mb-2"> <strong class="text-gray-900">${comment.name}</strong> <span class="text-sm text-gray-500">•</span> <span class="text-sm text-gray-500">${new Date(comment.date).toLocaleDateString('fr-FR')}</span> </div> <p class="text-gray-700">${comment.message}</p> </div> `).join(''); } else { commentsList.innerHTML = '<p class="text-gray-500 italic">Aucun commentaire pour le moment. Soyez le premier à commenter !</p>'; } } catch (error) { console.error('Erreur lors du chargement des commentaires:', error); } } // Gérer la soumission du formulaire document.getElementById('comment-form').addEventListener('submit', async function(e) { e.preventDefault(); const formData = { slug: articleSlug, name: document.getElementById('comment-name').value, email: document.getElementById('comment-email').value, message: document.getElementById('comment-message').value }; const submitBtn = this.querySelector('button[type="submit"]'); const originalText = submitBtn.innerHTML; submitBtn.disabled = true; submitBtn.innerHTML = '<i class="fas fa-spinner fa-spin"></i> Envoi en cours...'; try { const response = await fetch('/gestion-commentaires/submit-comment.php', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify(formData) }); const result = await response.json(); const messageDiv = document.getElementById('comment-status'); if (result.success) { messageDiv.className = 'mt-4 p-4 bg-green-100 border border-green-400 text-green-700 rounded-lg'; messageDiv.textContent = 'Merci ! Votre commentaire aété soumis et sera publié après modération.'; messageDiv.classList.remove('hidden'); this.reset(); } else { messageDiv.className = 'mt-4 p-4 bg-red-100 border border-red-400 text-red-700 rounded-lg'; messageDiv.textContent = result.message || 'Une erreur est survenue. Veuillez réessayer.'; messageDiv.classList.remove('hidden'); } } catch (error) { const messageDiv = document.getElementById('comment-status'); messageDiv.className = 'mt-4 p-4 bg-red-100 border border-red-400 text-red-700 rounded-lg'; messageDiv.textContent = 'Erreur de connexion. Veuillez réessayer plus tard.'; messageDiv.classList.remove('hidden'); } finally { submitBtn.disabled = false; submitBtn.innerHTML = originalText; } }); // Charger les commentaires au chargement de la page loadComments(); </script> <footer class="bg-gray-950 text-gray-400 py-12"> <div class="container mx-auto px-6"> <div class="flex flex-col md:flex-row justify-between items-center"> <div class="mb-6 md:mb-0 flex items-start gap-4"> <img src="/images/logo.png" alt="Logo VRAIVEX" class="h-32 w-32 md:h-40 md:w-40 object-contain"> <div> <a href="#" class="text-2xl font-bold block"> <span class="gradient-text">VRAIVEX</span> </a> <p class="mt-2 text-sm">Automatisation, IA et SEO au service de la performance e-commerce</p> </div> </div> <div class="flex flex-col items-center md:items-end"> <div class="grid grid-cols-2 md:flex md:flex-wrap gap-3 md:space-x-6 mb-4 text-center md:text-right"> <a href="/#about" class="hover:text-white transition text-sm">À propos</a> <a href="/#services" class="hover:text-white transition text-sm">Services</a> <a href="/#prestations" class="hover:text-white transition text-sm">Prestations</a> <a href="/#bestsellers" class="hover:text-white transition text-sm">Best Sellers</a> <a href="/#brands" class="hover:text-white transition text-sm">Nos marques</a> <a href="/creation-site-ecommerce" class="hover:text-white transition text-sm">Création Sites</a> <a href="/seo-ecommerce" class="hover:text-white transition text-sm">SEO E-commerce</a> <a href="/partenaires" class="hover:text-white transition text-sm">Partenaires</a> <a href="/#contact" class="hover:text-white transition text-sm">Contact</a> </div> <p class="text-sm text-center md:text-right">© 2025 VRAIVEX. Tous droits réservés.</p> </div> </div> </div> </footer> <!-- Back to Top Button --> <button id="backToTop" class="fixed bottom-8 right-8 bg-gradient-to-r from-purple-600 to-blue-600 text-white p-4 rounded-full shadow-lg hover:shadow-xl transform hover:scale-110 transition-all duration-300 z-50 hidden"> <i class="fas fa-arrow-up text-xl"></i> </button> <script> // Header scroll effect window.addEventListener('scroll', function() { const header = document.getElementById('header'); if (window.scrollY > 100) { header.classList.add('header-scrolled'); } else { header.classList.remove('header-scrolled'); } }); // Smooth scrolling for anchor links document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); document.querySelector(this.getAttribute('href')).scrollIntoView({ behavior: 'smooth' }); }); }); // Mobile menu toggle const mobileMenuButton = document.getElementById('mobileMenuButton'); const mobileMenu = document.getElementById('mobileMenu'); const menuIcon = document.getElementById('menuIcon'); if (mobileMenuButton && mobileMenu) { mobileMenuButton.addEventListener('click', function() { mobileMenu.classList.toggle('hidden'); // Toggle icon between bars and times if (mobileMenu.classList.contains('hidden')) { menuIcon.classList.remove('fa-times'); menuIcon.classList.add('fa-bars'); } else { menuIcon.classList.remove('fa-bars'); menuIcon.classList.add('fa-times'); } }); // Close menu when clicking on a link const mobileLinks = mobileMenu.querySelectorAll('a'); mobileLinks.forEach(link => { link.addEventListener('click', function() { mobileMenu.classList.add('hidden'); menuIcon.classList.remove('fa-times'); menuIcon.classList.add('fa-bars'); }); }); } // Brands Carousel const brandsCarousel = document.getElementById('brandsCarousel'); const brandsContainer = document.getElementById('brandsContainer'); const brandsPrevBtn = document.getElementById('brandsPrevBtn'); const brandsNextBtn = document.getElementById('brandsNextBtn'); const brandsPrevBtnMobile = document.getElementById('brandsPrevBtnMobile'); const brandsNextBtnMobile = document.getElementById('brandsNextBtnMobile'); if (brandsContainer && brandsCarousel) { let currentIndex = 0; const cards = brandsContainer.querySelectorAll('.brand-card'); const cardsPerView = { mobile: 1, tablet: 2, desktop: 3, large: 4 }; function getCardsPerView() { const width = window.innerWidth; if (width >= 1280) return cardsPerView.large; if (width >= 1024) return cardsPerView.desktop; if (width >= 768) return cardsPerView.tablet; return cardsPerView.mobile; } function updateCarousel() { const cardsPerViewCount = getCardsPerView(); const containerWidth = brandsCarousel.offsetWidth; const cardWidth = containerWidth / cardsPerViewCount; const maxIndex = Math.max(0, cards.length - cardsPerViewCount); currentIndex = Math.min(currentIndex, maxIndex); brandsContainer.style.transform = `translateX(-${currentIndex * cardWidth}px)`; // Update button states const isAtStart = currentIndex === 0; const isAtEnd = currentIndex >= maxIndex; if (brandsPrevBtn) { brandsPrevBtn.style.opacity = isAtStart ? '0.5' : '1'; brandsPrevBtn.style.cursor = isAtStart ? 'not-allowed' : 'pointer'; } if (brandsNextBtn) { brandsNextBtn.style.opacity = isAtEnd ? '0.5' : '1'; brandsNextBtn.style.cursor = isAtEnd ? 'not-allowed' : 'pointer'; } if (brandsPrevBtnMobile) { brandsPrevBtnMobile.style.opacity = isAtStart ? '0.5' : '1'; brandsPrevBtnMobile.style.cursor = isAtStart ? 'not-allowed' : 'pointer'; } if (brandsNextBtnMobile) { brandsNextBtnMobile.style.opacity = isAtEnd ? '0.5' : '1'; brandsNextBtnMobile.style.cursor = isAtEnd ? 'not-allowed' : 'pointer'; } } function nextSlide() { const cardsPerViewCount = getCardsPerView(); const maxIndex = Math.max(0, cards.length - cardsPerViewCount); if (currentIndex < maxIndex) { currentIndex++; updateCarousel(); } } function prevSlide() { if (currentIndex > 0) { currentIndex--; updateCarousel(); } } // Event listeners if (brandsNextBtn) brandsNextBtn.addEventListener('click', nextSlide); if (brandsPrevBtn) brandsPrevBtn.addEventListener('click', prevSlide); if (brandsNextBtnMobile) brandsNextBtnMobile.addEventListener('click', nextSlide); if (brandsPrevBtnMobile) brandsPrevBtnMobile.addEventListener('click', prevSlide); // Set responsive width for cards function setCardWidths() { const cardsPerViewCount = getCardsPerView(); const containerWidth = brandsCarousel.offsetWidth; const gap = 24; // 24px gap const cardWidth = (containerWidth - (gap * (cardsPerViewCount - 1))) / cardsPerViewCount; cards.forEach(card => { card.style.width = `${cardWidth}px`; card.style.flexShrink = '0'; }); } // Initialize setCardWidths(); updateCarousel(); // Update on resize let resizeTimeout; window.addEventListener('resize', function() { clearTimeout(resizeTimeout); resizeTimeout = setTimeout(function() { setCardWidths(); currentIndex = 0; updateCarousel(); }, 250); }); } </script> <script> // Header scroll effect window.addEventListener('scroll', function() { const header = document.getElementById('header'); if (window.scrollY > 50) { header.classList.add('header-scrolled'); } else { header.classList.remove('header-scrolled'); } }); // Scroll animations const observerOptions = { threshold: 0.1, rootMargin: '0px 0px -50px 0px' }; const observer = new IntersectionObserver(function(entries) { entries.forEach(entry => { if (entry.isIntersecting) { entry.target.classList.add('visible'); } }); }, observerOptions); // Observe all fade-in-up elements document.querySelectorAll('.fade-in-up').forEach(el => { observer.observe(el); }); // Smooth scroll for anchor links document.querySelectorAll('a[href^="#"]').forEach(anchor => { anchor.addEventListener('click', function (e) { e.preventDefault(); const target = document.querySelector(this.getAttribute('href')); if (target) { target.scrollIntoView({ behavior: 'smooth', block: 'start' }); } }); }); // Counter animation for stats function animateCounter(element, target, duration = 2000) { let start = 0; const increment = target / (duration / 16); const timer = setInterval(() => { start += increment; if (start >= target) { element.textContent = target + (element.textContent.includes('+') ? '+' : '') + (element.textContent.includes('K') ? 'K€' : ''); clearInterval(timer); } else { element.textContent = Math.floor(start) + (element.textContent.includes('+') ? '+' : '') + (element.textContent.includes('K') ? 'K€' : ''); } }, 16); } // Observe stats section const statsObserver = new IntersectionObserver(function(entries) { entries.forEach(entry => { if (entry.isIntersecting && !entry.target.classList.contains('animated')) { entry.target.classList.add('animated'); const statsCards = entry.target.querySelectorAll('.stats-card'); statsCards.forEach((card, index) => { setTimeout(() => { card.style.opacity = '0'; card.style.transform = 'translateY(20px)'; setTimeout(() => { card.style.transition = 'all 0.6s ease'; card.style.opacity = '1'; card.style.transform = 'translateY(0)'; }, 100); }, index * 100); }); } }); }, { threshold: 0.3 }); const statsSection = document.querySelector('section.bg-gradient-to-r'); if (statsSection) { statsObserver.observe(statsSection); } </script> <script> // Gestion du formulaire de contact const contactForm = document.getElementById('contact-form'); const formMessage = document.getElementById('form-message'); const submitBtn = document.getElementById('submit-btn'); if (contactForm) { contactForm.addEventListener('submit', async function(e) { e.preventDefault(); // Désactiver le bouton pendant l'envoi submitBtn.disabled = true; submitBtn.textContent = 'Envoi en cours...'; // Récupérer les données du formulaire const formData = new FormData(contactForm); try { // Vérifier que les données sont bien dans le FormData const formDataObj = { name: formData.get('name'), email: formData.get('email'), subject: formData.get('subject'), message: formData.get('message') }; console.log('Données du formulaire:', formDataObj); // Vérifier que tous les champs sont remplis if (!formDataObj.name || !formDataObj.email || !formDataObj.subject || !formDataObj.message) { formMessage.classList.remove('hidden'); formMessage.className = 'mb-6 p-4 rounded-lg bg-red-600 text-white'; formMessage.textContent = 'Veuillez remplir tous les champs du formulaire.'; submitBtn.disabled = false; submitBtn.textContent = 'Envoyer le message'; return; } // Essayer d'abord avec JSON (plus fiable) // Siça ne fonctionne pas, on essaiera avec FormData let response; try { // Méthode 1 : Envoyer en JSON (plus fiable selon les forums) response = await fetch('gestion-formulaire-contact/send-email-json.php', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(formDataObj) }); } catch (jsonError) { console.warn('Erreur avec JSON, essai avec FormData:', jsonError); // Méthode 2 : Fallback avec FormData response = await fetch('send-email.php', { method: 'POST', body: formData }); } // Lire le texte de la réponse d'abord pour déboguer const responseText = await response.text(); console.log('Réponse serveur:', responseText.substring(0, 500)); // Vérifier si la réponse est OK if (!response.ok) { // Essayer de parser le JSON d'erreur try { const errorResult = JSON.parse(responseText); // Afficher le message d'erreur du serveur directement à l'utilisateur formMessage.classList.remove('hidden'); formMessage.className = 'mb-6 p-4 rounded-lg bg-red-600 text-white'; formMessage.textContent = errorResult.message || `Erreur ${response.status}: ${response.statusText}`; submitBtn.disabled = false; submitBtn.textContent = 'Envoyer le message'; return; // Sortir de la fonction pour ne pas continuer } catch (e) { throw new Error(`Erreur HTTP ${response.status}: ${response.statusText}. Réponse: ${responseText.substring(0, 200)}`); } } // Vérifier si PHP n'est pas exécuté (le serveur renvoie le code PHP brut) if (responseText.trim().startsWith('<?php') || responseText.includes('<?php')) { console.error('ERREUR: PHP n\'est pas exécuté par le serveur. Le code PHP est renvoyé brut.'); console.error('Le serveur web n\'est pas configuré pour exécuter PHP.'); // Utiliser la solution de secours : sauvegarder dans localStorage const messageData = { name: formData.get('name'), email: formData.get('email'), subject: formData.get('subject'), message: formData.get('message'), timestamp: new Date().toISOString() }; // Sauvegarder dans localStorage comme solution de secours const savedMessages = JSON.parse(localStorage.getItem('vraivex_messages') || '[]'); savedMessages.push(messageData); localStorage.setItem('vraivex_messages', JSON.stringify(savedMessages)); // Afficher un message spécial formMessage.classList.remove('hidden'); formMessage.className = 'mb-6 p-4 rounded-lg bg-yellow-600 text-white'; formMessage.innerHTML = '⚠️ PHP n\'est pas configuré sur le serveur. Votre message aété sauvegardé localement. <br>Veuillez nous contacter directement à <strong>contact@vraivex.fr</strong> ou consulter les messages sauvegardés dans la console du navigateur.'; // Afficher les messages sauvegardés dans la console console.log('Messages sauvegardés localement:', savedMessages); console.log('Pour consulter les messages, tapez dans la console: JSON.parse(localStorage.getItem("vraivex_messages"))'); return; // Sortir de la fonction } // Essayer de parser le JSON let result; try { result = JSON.parse(responseText); } catch (parseError) { console.error('Erreur de parsing JSON:', parseError); console.error('Réponse reçue:', responseText.substring(0, 500)); throw new Error('Le serveur a renvoyé une réponse invalide. Vérifiez la console pour plus de détails.'); } // Afficher le message de résultat formMessage.classList.remove('hidden'); if (result.success) { formMessage.className = 'mb-6 p-4 rounded-lg bg-green-600 text-white'; formMessage.textContent = 'Message envoyé avec succès ! Nous vous répondrons dans les plus brefs délais.'; contactForm.reset(); } else { formMessage.className = 'mb-6 p-4 rounded-lg bg-red-600 text-white'; let errorMsg = result.message || 'Une erreur est survenue. Veuillez réessayer.'; // Afficher le message de debug en développement (à retirer en production) if (result.debug) { console.error('Erreur détaillée:', result.debug); } formMessage.textContent = errorMsg; } } catch (error) { formMessage.classList.remove('hidden'); formMessage.className = 'mb-6 p-4 rounded-lg bg-red-600 text-white'; // Message d'erreur plus détaillé pour le débogage let errorMsg = 'Une erreur est survenue lors de la communication avec le serveur. '; errorMsg += 'Veuillez réessayer plus tard ou nous contacter directement à contact@vraivex.fr'; // En mode développement, afficher plus de détails if (error.message) { console.error('Erreur détaillée:', error); console.error('Message:', error.message); console.error('Stack:', error.stack); } formMessage.textContent = errorMsg; } finally { // Réactiver le bouton submitBtn.disabled = false; submitBtn.textContent = 'Envoyer le message'; // Masquer le message après 5 secondes setTimeout(() => { formMessage.classList.add('hidden'); }, 5000); } }); } // Back to Top Button functionality const backToTopButton = document.getElementById('backToTop'); // Show/hide button based on scroll position window.addEventListener('scroll', () => { if (window.pageYOffset > 300) { backToTopButton.classList.remove('hidden'); } else { backToTopButton.classList.add('hidden'); } }); // Smooth scroll to top when clicked backToTopButton.addEventListener('click', () => { window.scrollTo({ top: 0, behavior: 'smooth' }); }); </script> </body> </html>