İçeriğe geç
nextjs

Next.js Sitemap ve Robots.txt Oluşturma

Tarık Tunç
Next.js Sitemap ve Robots.txt Oluşturma

Next.js Sitemap ve Robots.txt Oluşturma

Arama motorlarının sitenizi doğru ve verimli bir şekilde taraması için sitemap.xml ve robots.txt dosyaları kritik öneme sahiptir. Next.js App Router, bu dosyaları oluşturmak için yerleşik destek sunar. Bu yazıda statik ve dinamik sitemap oluşturmayı, robots.txt yapılandırmasını ve SEO en iyi pratiklerini adım adım inceleyeceğiz.

Robots.txt Nedir?

Robots.txt, arama motoru bot'larına sitenizin hangi bölümlerini tarayıp hangilerini atlayacağını bildiren bir dosyadır. Kök dizinde (/robots.txt) bulunmalıdır.

Statik robots.txt

# app/robots.txt (statik dosya)
User-Agent: *
Allow: /
Disallow: /api/
Disallow: /admin/
Disallow: /private/

Sitemap: https://example.com/sitemap.xml

Dinamik robots.txt

// app/robots.js
export default function robots() {
  const baseUrl = process.env.NEXT_PUBLIC_SITE_URL || 'https://example.com';

  return {
    rules: [
      {
        userAgent: '*',
        allow: '/',
        disallow: ['/api/', '/admin/', '/private/', '/_next/'],
      },
      {
        userAgent: 'GPTBot',
        disallow: '/',
      },
      {
        userAgent: 'Googlebot',
        allow: '/',
        disallow: '/api/',
      },
    ],
    sitemap: `${baseUrl}/sitemap.xml`,
  };
}

Sitemap Nedir?

Sitemap, arama motorlarına sitenizin tüm sayfalarını, güncelleme sıklığını ve önceliğini bildiren XML dosyasıdır. Büyük sitelerde arama motorlarının tüm sayfaları keşfetmesini kolaylaştırır.

Statik Sitemap

// app/sitemap.js
export default function sitemap() {
  const baseUrl = 'https://example.com';

  return [
    {
      url: baseUrl,
      lastModified: new Date(),
      changeFrequency: 'daily',
      priority: 1.0,
    },
    {
      url: `${baseUrl}/hakkimizda`,
      lastModified: new Date(),
      changeFrequency: 'monthly',
      priority: 0.8,
    },
    {
      url: `${baseUrl}/blog`,
      lastModified: new Date(),
      changeFrequency: 'weekly',
      priority: 0.9,
    },
    {
      url: `${baseUrl}/iletisim`,
      lastModified: new Date(),
      changeFrequency: 'yearly',
      priority: 0.5,
    },
  ];
}

Dinamik Sitemap (Veritabanından)

// app/sitemap.js
import { prisma } from '@/lib/prisma';

export default async function sitemap() {
  const baseUrl = process.env.NEXT_PUBLIC_SITE_URL;

  // Statik sayfalar
  const staticPages = [
    { url: baseUrl, lastModified: new Date(), priority: 1.0 },
    { url: `${baseUrl}/hakkimizda`, lastModified: new Date(), priority: 0.8 },
    { url: `${baseUrl}/iletisim`, lastModified: new Date(), priority: 0.5 },
  ];

  // Blog yazıları — veritabanından
  const posts = await prisma.post.findMany({
    where: { published: true },
    select: { slug: true, updatedAt: true },
    orderBy: { updatedAt: 'desc' },
  });

  const blogPages = posts.map((post) => ({
    url: `${baseUrl}/blog/${post.slug}`,
    lastModified: post.updatedAt,
    changeFrequency: 'weekly',
    priority: 0.7,
  }));

  // Ürünler — veritabanından
  const products = await prisma.product.findMany({
    where: { active: true },
    select: { slug: true, updatedAt: true },
  });

  const productPages = products.map((product) => ({
    url: `${baseUrl}/urunler/${product.slug}`,
    lastModified: product.updatedAt,
    changeFrequency: 'daily',
    priority: 0.8,
  }));

  // Kategoriler
  const categories = await prisma.category.findMany({
    select: { slug: true },
  });

  const categoryPages = categories.map((cat) => ({
    url: `${baseUrl}/kategori/${cat.slug}`,
    lastModified: new Date(),
    changeFrequency: 'weekly',
    priority: 0.6,
  }));

  return [...staticPages, ...blogPages, ...productPages, ...categoryPages];
}

Büyük Siteler İçin Sitemap Index

50.000'den fazla URL veya 50MB'dan büyük sitemap dosyaları için sitemap index kullanmalısınız:

// app/sitemap.js
export async function generateSitemaps() {
  const totalProducts = await prisma.product.count();
  const sitemapCount = Math.ceil(totalProducts / 50000);

  // Her biri 50.000 URL içeren sitemap'ler
  return Array.from({ length: sitemapCount }, (_, i) => ({ id: i }));
}

export default async function sitemap({ id }) {
  const baseUrl = process.env.NEXT_PUBLIC_SITE_URL;
  const start = id * 50000;

  const products = await prisma.product.findMany({
    where: { active: true },
    select: { slug: true, updatedAt: true },
    skip: start,
    take: 50000,
    orderBy: { id: 'asc' },
  });

  return products.map((product) => ({
    url: `${baseUrl}/urunler/${product.slug}`,
    lastModified: product.updatedAt,
  }));
}

// Bu, aşağıdaki URL'leri oluşturur:
// /sitemap/0.xml
// /sitemap/1.xml
// /sitemap/2.xml
// ve bir sitemap index dosyası

Çok Dilli Siteler İçin Sitemap

// app/sitemap.js
export default async function sitemap() {
  const baseUrl = 'https://example.com';
  const locales = ['tr', 'en', 'de'];

  const posts = await getPosts();

  const entries = posts.flatMap((post) =>
    locales.map((locale) => ({
      url: `${baseUrl}/${locale}/blog/${post.slug}`,
      lastModified: post.updatedAt,
      alternates: {
        languages: Object.fromEntries(
          locales.map((l) => [l, `${baseUrl}/${l}/blog/${post.slug}`])
        ),
      },
    }))
  );

  return entries;
}

Bu yapılandırma, aşağıdaki XML çıktısını üretir:

<url>
  <loc>https://example.com/tr/blog/react-rehberi</loc>
  <xhtml:link rel="alternate" hreflang="tr" href="https://example.com/tr/blog/react-rehberi" />
  <xhtml:link rel="alternate" hreflang="en" href="https://example.com/en/blog/react-rehberi" />
  <xhtml:link rel="alternate" hreflang="de" href="https://example.com/de/blog/react-rehberi" />
</url>

Google Search Console'a Sitemap Gönderme

  1. Google Search Console'a giriş yapın
  2. Sol menüden "Sitemaps" seçin
  3. Sitemap URL'ini girin: https://example.com/sitemap.xml
  4. "Gönder" butonuna tıklayın
  5. Durumun "Başarılı" olmasını bekleyin
İpucu: Sitemap'i Google Search Console'a gönderdikten sonra düzenli olarak kontrol edin. "Bulunamadı" veya "Okunamadı" hataları varsa, sitemap URL'inin doğru olduğundan ve robots.txt'in sitemap'i engellemediğinden emin olun.

Doğrulama ve Test

# Sitemap'i kontrol etme
curl https://example.com/sitemap.xml

# Robots.txt kontrol
curl https://example.com/robots.txt

# Google Search Console URL Denetim Aracı ile test
# Lighthouse SEO denetimi ile kontrol
Uyarı: Sitemap'te sadece canonical URL'leri listeleyin. Duplicate content, redirect edilen sayfalar, 404 dönen URL'ler veya noindex sayfalar sitemap'e dahil edilmemelidir. Bu, arama motoru bütçenizi (crawl budget) boşa harcar.

Sonuç

Next.js App Router, sitemap.xml ve robots.txt oluşturmayı son derece kolaylaştırır. JavaScript fonksiyonları ile dinamik sitemap'ler oluşturabilir, veritabanından URL'leri çekebilir ve çok dilli siteler için hreflang desteği ekleyebilirsiniz. Büyük siteler için sitemap index yapısını kullanın, robots.txt ile crawl bütçenizi optimize edin ve Google Search Console ile sitemap'inizi takip edin. Bu adımlar, arama motorlarının sitenizi daha iyi anlamasını ve dizinlemesini sağlar.

Kaynaklar

İlgili Yazılar