Guide Next.js Sitemap et robots.txt : indexer rapidement votre site

Le site est en ligne. Vous tapez son nom dans Google et… rien.
Vous actualisez, changez de mots-clés — toujours vide. Dans Google Search Console, le Sitemap soumis affiche « Impossible de récupérer ». Sans trafic organique, même le meilleur contenu reste invisible.
J’ai déjà vécu ça. Sur mon premier projet Next.js, j’avais suivi un tutoriel pour le Sitemap, mais Google ne le récupérait pas. Une semaine de tests — le problème venait de robots.txt qui bloquait tout le site. Une erreur basique, mais frustrante.
Cet article rassemble les pièges que j’ai rencontrés, la doc consultée et les configs testées. Je vous explique clairement le rôle de ces deux fichiers, trois méthodes de génération de Sitemap, les erreurs fréquentes, plus un cas réel d’échec et comment s’en remettre. Si votre site n’est pas indexé, si le Sitemap renvoie une erreur ou si vous ne savez pas gérer les pages dynamiques, ce guide devrait vous faire gagner du temps.
Pourquoi Sitemap et robots.txt ?
Rôle du Sitemap
Le Sitemap est une « carte » pour les moteurs de recherche : quelles pages existent, à quelle fréquence elles changent, lesquelles comptent le plus. Sans Sitemap, les crawlers découvrent les pages seuls — surtout les pages profondes ou dynamiques, parfois introuvables pendant des mois.
Selon les données du secteur, un site avec Sitemap voit sa vitesse d’indexation augmenter d’environ 40 %. Pour un nouveau site, l’écart est encore plus net : une semaine contre un mois.
Rôle de robots.txt
robots.txt indique aux moteurs ce qu’ils peuvent ou ne peuvent pas crawler. Back-office, API, fichiers de build — inutiles en index. robots.txt oriente le budget de crawl vers les pages utiles.
Point crucial : ne pas configurer robots.txt vaut mieux qu’une mauvaise config. Trop souvent, on voulait bloquer un répertoire et on a bloqué tout le site, qui disparaît de Google. Testez, testez, retestez.
Trois méthodes pour générer un Sitemap dans Next.js
Avec l’App Router (Next.js 13+), les options ont évolué. Du plus simple au plus flexible :
Méthode 1 : sitemap.ts natif App Router
Cas d’usage : Next.js 13+, peu de pages (dizaines à centaines)
Méthode officielle recommandée, sans dépendance supplémentaire. Créez sitemap.ts dans app :
// app/sitemap.ts
import { MetadataRoute } from 'next'
export default function sitemap(): MetadataRoute.Sitemap {
return [
{
url: 'https://yourdomain.com',
lastModified: new Date(),
changeFrequency: 'yearly',
priority: 1,
},
{
url: 'https://yourdomain.com/about',
lastModified: new Date(),
changeFrequency: 'monthly',
priority: 0.8,
},
{
url: 'https://yourdomain.com/blog',
lastModified: new Date(),
changeFrequency: 'weekly',
priority: 0.5,
},
]
}
Après déploiement, https://yourdomain.com/sitemap.xml affiche le Sitemap généré.
Avantages :
- Support officiel, stable
- Aucune dépendance extra
- Typage TypeScript
Inconvénients :
- Pages statiques à maintenir manuellement
- Pages dynamiques : récupération des données dans le code
Méthode 2 : package next-sitemap
Cas d’usage : automatisation, multi-environnement, très grand nombre de pages
next-sitemap est l’outil communautaire le plus populaire pour les Sitemaps.
Installation :
npm install next-sitemap
Fichier de config next-sitemap.config.js :
/** @type {import('next-sitemap').IConfig} */
module.exports = {
siteUrl: process.env.SITE_URL || 'https://yourdomain.com',
generateRobotsTxt: true, // génère robots.txt automatiquement
sitemapSize: 50000, // max 50 000 URL par Sitemap
exclude: ['/admin/*', '/api/*', '/secret'], // exclure certains chemins
robotsTxtOptions: {
policies: [
{
userAgent: '*',
allow: '/',
disallow: ['/admin', '/api'],
},
],
additionalSitemaps: [
'https://yourdomain.com/server-sitemap.xml', // Sitemap dynamique
],
},
}
Script dans package.json :
{
"scripts": {
"build": "next build",
"postbuild": "next-sitemap"
}
}
Chaque npm run build régénère le Sitemap.
Avantages :
- Puissant, Sitemaps multiples
- robots.txt auto
- Routes dynamiques
- Multi-environnement
Inconvénients :
- Dépendance supplémentaire
- Config un peu plus complexe
Méthode 3 : génération manuelle via route API
Cas d’usage : personnalisation maximale ou mise à jour en temps réel
Route Handler dans l’App Router :
// app/sitemap.xml/route.ts
import { NextResponse } from 'next/server'
export async function GET() {
// Récupérer les données depuis la BDD ou le CMS
const posts = await fetchAllPosts()
const sitemap = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://yourdomain.com</loc>
<lastmod>${new Date().toISOString()}</lastmod>
<priority>1.0</priority>
</url>
${posts.map(post => `
<url>
<loc>https://yourdomain.com/blog/${post.slug}</loc>
<lastmod>${post.updatedAt}</lastmod>
<priority>0.7</priority>
</url>
`).join('')}
</urlset>`
return new NextResponse(sitemap, {
status: 200,
headers: {
'Content-Type': 'application/xml',
'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate',
},
})
}
Avantages :
- Contrôle total
- Génération en temps réel
- Logique complexe possible
Inconvénients :
- XML à écrire soi-même
- Performance à optimiser
- Cache à gérer manuellement
Comparaison des trois méthodes
| Méthode | Cas d’usage | Difficulté | Flexibilité | Recommandation |
|---|---|---|---|---|
| App Router natif | Petit site statique | ⭐ | ⭐⭐ | ⭐⭐⭐⭐ |
| next-sitemap | Projets moyens/grands | ⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| Route API | Personnalisation max | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ |
La plupart de mes projets utilisent next-sitemap : une config et c’est réglé.
Routes dynamiques en pratique : Sitemap pour un blog
Scénario fréquent — articles, fiches produit, profils utilisateur : comment les ajouter au Sitemap ?
Avec sitemap.ts natif App Router
// app/sitemap.ts
import { MetadataRoute } from 'next'
import { getAllPosts } from '@/lib/posts'
export default async function sitemap(): MetadataRoute.Sitemap {
// Pages statiques
const staticPages = [
{
url: 'https://yourdomain.com',
lastModified: new Date(),
changeFrequency: 'yearly' as const,
priority: 1,
},
{
url: 'https://yourdomain.com/about',
lastModified: new Date(),
changeFrequency: 'monthly' as const,
priority: 0.8,
},
]
// Récupérer tous les articles
const posts = await getAllPosts()
const postPages = posts.map(post => ({
url: `https://yourdomain.com/blog/${post.slug}`,
lastModified: new Date(post.updatedAt),
changeFrequency: 'weekly' as const,
priority: 0.7,
}))
return [...staticPages, ...postPages]
}
// Délai de revalidation (ISR)
export const revalidate = 3600 // régénération toutes les heures
Points clés :
changeFrequencyetpriorityavec assertionas constexport const revalidatepour l’ISRlastModifiedbasé sur la vraie date de mise à jour de l’article
Grands sites : plus de 50 000 URL ?
Google limite un Sitemap à 50 000 URL. Au-delà, divisez en plusieurs Sitemaps.
Fonction generateSitemaps :
// app/sitemap.ts
import { MetadataRoute } from 'next'
// Générer plusieurs Sitemaps
export async function generateSitemaps() {
const totalPosts = await getTotalPostsCount()
const sitemapsCount = Math.ceil(totalPosts / 50000)
return Array.from({ length: sitemapsCount }, (_, i) => ({
id: i,
}))
}
// Contenu de chaque Sitemap
export default async function sitemap({
id,
}: {
id: number
}): Promise<MetadataRoute.Sitemap> {
const start = id * 50000
const end = start + 50000
const posts = await getPosts(start, end)
return posts.map(post => ({
url: `https://yourdomain.com/blog/${post.slug}`,
lastModified: new Date(post.updatedAt),
priority: 0.7,
}))
}
Cela produit plusieurs Sitemaps :
sitemap/0.xmlsitemap/1.xmlsitemap/2.xml- …
Next.js génère automatiquement un fichier index sitemap.xml listant tous les sous-Sitemaps.
Configuration complète de robots.txt
Exemple de base
robots.txt minimal :
# Autoriser tous les crawlers sur tout le contenu
User-agent: *
Allow: /
# Emplacement du Sitemap
Sitemap: https://yourdomain.com/sitemap.xml
En production, une config plus fine :
User-agent: *
Allow: /
# Interdire ces répertoires
Disallow: /_next/
Disallow: /api/
Disallow: /admin/
Disallow: /dashboard/
# Interdire certains types de fichiers
Disallow: /*.json$
Disallow: /*.xml$
Disallow: /*?* # URL avec paramètres de requête
# Sitemap
Sitemap: https://yourdomain.com/sitemap.xml
Remarques :
/_next/: fichiers de build Next.js, inutiles en index/api/: les API ne doivent pas être indexées/admin/et/dashboard/: back-office à exclure/*.json$: les JSON n’ont pas besoin d’être indexés- La ligne Sitemap compte : sans elle, les moteurs ne savent pas où trouver votre Sitemap
Génération dynamique robots.txt dans Next.js
Avec robots.ts dans l’App Router :
// app/robots.ts
import { MetadataRoute } from 'next'
export default function robots(): MetadataRoute.Robots {
const baseUrl = 'https://yourdomain.com'
// En dev : bloquer tous les crawlers
if (process.env.NODE_ENV === 'development') {
return {
rules: {
userAgent: '*',
disallow: '/',
},
}
}
// Config production
return {
rules: [
{
userAgent: '*',
allow: '/',
disallow: [
'/_next/',
'/api/',
'/admin/',
'/dashboard/',
],
},
{
userAgent: 'GPTBot', // bloquer le crawler OpenAI
disallow: ['/'],
},
],
sitemap: `${baseUrl}/sitemap.xml`,
}
}
Intérêt du multi-environnement :
Dev et preview ne doivent pas être indexés ; les variables d’environnement évitent l’indexation de contenu de test.
Beaucoup utilisent la même config en prod et en dev — risque sérieux. Je recommande :
// app/robots.ts
import { MetadataRoute } from 'next'
export default function robots(): MetadataRoute.Robots {
const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://yourdomain.com'
const isProduction = process.env.NODE_ENV === 'production'
const isDeployPreview = process.env.NEXT_PUBLIC_VERCEL_ENV === 'preview'
// Hors production : bloquer tous les crawlers
if (!isProduction || isDeployPreview) {
return {
rules: {
userAgent: '*',
disallow: '/',
},
}
}
// Autoriser les crawlers uniquement en production
return {
rules: [
{
userAgent: '*',
allow: '/',
disallow: [
'/_next/',
'/api/',
'/admin/',
'/dashboard/',
'/*.json$',
],
},
{
userAgent: 'GPTBot',
disallow: ['/'],
},
],
sitemap: `${baseUrl}/sitemap.xml`,
}
}
Ainsi, pas de crawl de contenu de test ni de blocage accidentel en production.
Pièges courants à éviter
Erreur 1 : bloquer tout le site par erreur
# ❌ Incorrect
User-agent: *
Disallow: /
Cela bloque tout le site ! Correct :
# ✅ Correct
User-agent: *
Allow: /
Disallow: /admin/
Erreur 2 : oublier la référence au Sitemap
Sitemap configuré, mais non déclaré dans robots.txt — les moteurs ne le trouvent pas.
# ❌ Il manque cette ligne
Sitemap: https://yourdomain.com/sitemap.xml
Erreur 3 : format de chemin incorrect
# ❌ Le chemin ne commence pas par /
Disallow: _next/
# ✅ Le chemin doit commencer par /
Disallow: /_next/
Erreur 4 : restrictions excessives
# ❌ Bloquer aussi les images
Disallow: /*.jpg$
Disallow: /*.png$
Les images font partie du contenu ; laissez-les être crawlées sauf raison particulière.
Méthodes de test :
- Ouvrir
https://yourdomain.com/robots.txtet vérifier le contenu - Outil de test robots.txt dans Google Search Console
- Tester si une URL donnée est autorisée au crawl
Intégration et validation Google Search Console
Une fois Sitemap et robots.txt configurés, soumettez-les dans Search Console pour accélérer la découverte et l’indexation.
Ajouter le site à Search Console
- Aller sur Google Search Console
- Cliquer « Ajouter une propriété »
- Choisir validation par « Domaine » ou « Préfixe d’URL »
DNS recommandé :
- Ajouter un enregistrement TXT chez le registrar
- Attendre quelques minutes la propagation DNS
- Revenir dans Search Console et valider
Ou validation par fichier HTML :
Placer dans public le fichier fourni par Google, ex. google1234567890abcdef.html.
Soumettre le Sitemap
Après validation :
- Menu gauche → « Sitemaps »
- Saisir l’URL :
sitemap.xml - Cliquer « Envoyer »
Délai :
- Google ne traite pas immédiatement
- En général 1 à 7 jours avant le crawl
- L’état apparaît dans Search Console
Dépannage des erreurs courantes
Erreur 1 : « Impossible de récupérer le Sitemap »
Problème très fréquent. Causes possibles :
Cause 1 : Middleware qui bloque Googlebot
Un Middleware Next.js d’authentification peut bloquer Googlebot.
Solution :
// middleware.ts
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl
const userAgent = request.headers.get('user-agent') || ''
// Détecter un crawler
const isBot = /googlebot|bingbot|slurp|duckduckbot|baiduspider|yandexbot/i.test(
userAgent
)
// Sitemap et robots.txt accessibles à tous, y compris les crawlers
if (
pathname === '/robots.txt' ||
pathname === '/sitemap.xml' ||
pathname.startsWith('/sitemap-')
) {
return NextResponse.next()
}
// Laisser passer les crawlers
if (isBot) {
return NextResponse.next()
}
// Logique d'authentification pour les utilisateurs
const token = request.cookies.get('session-token')
if (!token && pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url))
}
return NextResponse.next()
}
export const config = {
matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
}
Les crawlers accèdent au Sitemap et robots.txt ; l’authentification reste active pour les visiteurs.
Cause 2 : problème de cache
Search Console peut mettre en cache un échec de récupération. Même après correction, « Impossible de récupérer » peut persister.
Solutions :
- Ajouter un paramètre horodaté :
sitemap.xml?v=20231220 - Attendre quelques jours le re-crawl
- Supprimer l’ancienne soumission et resoumettre
Cause 3 : XML mal formé
Vérifier le format :
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://yourdomain.com</loc>
<lastmod>2024-12-20</lastmod>
</url>
</urlset>
Attention :
- La déclaration XML commence par
<?xml(pas<xml) lastmodau format ISO 8601 (YYYY-MM-DD ou horodatage complet)
Utilisez un validateur XML en ligne pour contrôler.
Erreur 2 : « Soumis mais non indexé »
Sitemap OK, pages toujours absentes de l’index. Causes possibles :
- robots.txt bloque la page : vérifier la config
- Qualité de page : contenu trop court, dupliqué ou jugé faible
- Site trop récent : il faut du temps pour la confiance
- Pas de backlinks : difficile sans liens externes
Solutions :
- Outil « Inspection d’URL » dans Search Console
- Vérifier l’absence de balise meta
noindex - Contenu substantiel, au moins ~300 mots
- Obtenir quelques backlinks
Surveiller l’indexation
Après soumission, contrôler régulièrement :
- Rapport Couverture : pages indexées vs problèmes
- État de l’index : volume total indexé
- État du Sitemap : lecture correcte ou non
Pour une page non indexée, utiliser « Inspection d’URL » et demander un nouveau crawl.
Cas pratique : mes propres pièges
L’an dernier, j’ai repris un site e-commerce en ligne depuis 3 mois — aucune page dans Google.
Premier réflexe : robots.txt. Résultat :
User-agent: *
Disallow: /
Tout le site bloqué — depuis 3 mois. Le collègue déploiement jurait ne pas l’avoir touché ; un dev temporaire avait appliqué la config anti-crawl du staging en production.
Le mois suivant fut long. J’ai supprimé le mauvais robots.txt, généré le bon Sitemap, soumis à Search Console — puis l’attente. Une semaine avant la première apparition dans les résultats.
Depuis, je vérifie Sitemap et robots.txt avant chaque déploiement, avec des configs distinctes par environnement.
Autre épisode : Middleware JWT qui bloquait Googlebot. Search Console affichait « Impossible de récupérer le Sitemap » — j’ai cru à un problème de format XML avant de trouver le Middleware.
Le SEO paraît simple ; les détails comptent. Une petite erreur peut faire disparaître votre site des résultats.
Dépannage et optimisation
Checklist de test
Avant déploiement :
- Sitemap accessible (
https://yourdomain.com/sitemap.xml) - Format XML du Sitemap correct
- robots.txt accessible (
https://yourdomain.com/robots.txt) - robots.txt ne bloque pas les pages importantes
- robots.txt référence le Sitemap
- Sitemap inclut toutes les pages importantes
- Pages dynamiques mises à jour automatiquement dans le Sitemap
- Site validé dans Search Console
- Sitemap soumis à Search Console
- Middleware n’intercepte pas les crawlers
Optimisation des performances
Stratégie de cache Sitemap
Avec une route API pour le Sitemap, ajoutez du cache :
// app/sitemap.xml/route.ts
export const revalidate = 3600 // cache 1 heure
export async function GET() {
// ... génération du Sitemap
return new NextResponse(sitemap, {
headers: {
'Content-Type': 'application/xml',
'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate',
},
})
}
Mise à jour incrémentale vs reconstruction complète
- Petit site (< 1 000 pages) : reconstruction complète, simple
- Site moyen (1 000–10 000 pages) : ISR, revalidation horaire ou quotidienne
- Grand site (> 10 000 pages) : Sitemaps multiples, mise à jour incrémentale des parties modifiées
Configuration CDN
Avec Cloudflare ou autre CDN, cachez aussi Sitemap et robots.txt :
- En-têtes Cache-Control adaptés
- Autoriser le cache XML et TXT
- Purger le cache CDN après mise à jour du contenu
Résumé
Récapitulatif du flux :
-
Choisir la méthode Sitemap :
- Petit projet : App Router natif
- Moyen/grand : next-sitemap
- Personnalisation max : route API
-
Configurer robots.txt :
- Bloquer les répertoires inutiles
- Référencer le Sitemap
- Interdire le crawl en dev
-
Soumettre à Google Search Console :
- Valider la propriété
- Soumettre le Sitemap
- Surveiller l’indexation
-
Dépannage courant :
- Middleware ne doit pas bloquer les crawlers
- XML correctement formé
- Cache : paramètre horodaté si besoin
Configurer Sitemap et robots.txt n’est pas rocket science, mais les détails piègent. Ma première config m’a bloqué tout le site un mois entier.
Aujourd’hui, chaque nouveau projet passe par cette checklist — plus de mauvaises surprises. J’espère que cet article vous fera gagner du temps pour une indexation rapide.
Des questions ou retours d’expérience ? Laissez un commentaire !
Configuration complète Sitemap et robots.txt Next.js
Étapes SEO complètes, de la création des fichiers à la soumission Google Search Console
⏱️ Estimated time: 1 hr
- 1
Step 1: Créer un Sitemap dynamique
Créer app/sitemap.ts :
• Exporter une fonction default qui retourne un tableau sitemap
• Chaque entrée contient url, lastModified, changeFrequency, priority
• Fonction async possible pour récupérer des données dynamiques
Exemple :
export default async function sitemap() {
const posts = await getPosts()
return [
{
url: 'https://example.com',
lastModified: new Date(),
changeFrequency: 'yearly',
priority: 1,
},
...posts.map(post => ({
url: `https://example.com/posts/${post.id}`,
lastModified: post.updatedAt,
changeFrequency: 'weekly',
priority: 0.8,
}))
]
} - 2
Step 2: Créer robots.txt
Créer app/robots.ts :
• Exporter une fonction default qui retourne la config robots
• Configurer les crawlers autorisés/interdits
• Définir le chemin du sitemap
Exemple :
export default function robots() {
return {
rules: {
userAgent: '*',
allow: '/',
disallow: ['/api/', '/admin/'],
},
sitemap: 'https://example.com/sitemap.xml',
}
}
Attention : ne pas disallow par erreur tout le site - 3
Step 3: Vérifier la génération des fichiers
Contrôler les fichiers générés :
• Visiter /sitemap.xml pour le sitemap
• Visiter /robots.txt pour la config robots
• Confirmer que toutes les pages sont dans le sitemap
• Confirmer que robots.txt ne bloque pas le site par erreur
Outils de validation :
• Google Search Console
• Validateur XML en ligne
• Accès direct dans le navigateur pour vérifier le format - 4
Step 4: Soumettre à Google Search Console
Étapes :
1. Créer un compte Google Search Console
2. Ajouter une propriété (vérifier la propriété)
3. Soumettre l'URL sitemap.xml
4. Vérifier que robots.txt autorise le crawl
Méthodes de vérification :
• Upload d'un fichier HTML
• Balise HTML
• Enregistrement DNS
• Vérification via Google Analytics - 5
Step 5: Gérer les pages dynamiques
Routes dynamiques :
• Récupérer toutes les données des pages dynamiques dans sitemap.ts
• Générer une URL pour chaque page dynamique
• Définir le bon lastModified
Exemple :
const products = await getAllProducts()
const productUrls = products.map(product => ({
url: `https://example.com/products/${product.id}`,
lastModified: product.updatedAt,
changeFrequency: 'weekly' as const,
priority: 0.8,
})) - 6
Step 6: Surveiller et optimiser
Surveillance continue :
• Consulter régulièrement Google Search Console
• Vérifier l'état de soumission du sitemap
• Contrôler si robots.txt affecte le crawl
• Suivre l'indexation
Conseils d'optimisation :
• Mettre à jour le sitemap (ajouter les nouvelles pages)
• Définir un changeFrequency raisonnable
• Priorité élevée pour les pages importantes
• Vérifier régulièrement la config robots.txt
FAQ
Le Sitemap est-il obligatoire ?
Comment générer un Sitemap pour les routes dynamiques ?
Quelles conséquences d'une mauvaise config robots.txt ?
Combien de temps avant que Google crawle le Sitemap soumis ?
Comment valider que le Sitemap est correct ?
robots.txt peut-il bloquer un crawler spécifique ?
Le Sitemap doit-il inclure toutes les pages ?
11 min de lecture · Publié le: 20 déc. 2025 · Mis à jour le: 27 juil. 2026
Guide complet Next.js
Si vous arrivez depuis la recherche, le plus rapide est de passer à l’article précédent ou suivant de cette série.
Précédent
Guide SEO Next.js complet : Metadata API + données structurées en pratique
Guide complet pour configurer le SEO avec la Metadata API de Next.js 15 : données structurées, Open Graph, Twitter Cards, exemples de code et pièges à éviter pour multiplier le trafic.
Partie 28 sur 51
Suivant
Configuration TypeScript avancée pour Next.js : optimiser tsconfig et la sécurité des types
Guide pratique pour optimiser TypeScript dans Next.js : mode strict de tsconfig, routes typées, variables d'environnement typées — éliminez les any et améliorez l'expérience de développement.
Partie 30 sur 51



Commentaires
Connectez-vous avec GitHub pour laisser un commentaire