Next.js SEO Rehberi 2026: App Router ile Kurulum
Next.js App Router ile SEO kurulumu, Metadata API, sitemap.ts, JSON-LD, rendering stratejisi seçimi ve sık yapılan 7 hata tek rehberde bir arada.
İçindekiler
Next.js, React dünyasının en güçlü framework’ü. Ama 90+ projede gördüğümüz bir gerçek var: Next.js kullanmak tek başına SEO getirmiyor. Yanlış rendering stratejisi seçilmiş, metadata yarım bırakılmış bir Next.js sitesi, düzgün kurulmuş bir WordPress’ten daha kötü sıralanabiliyor.
İyi haber şu: App Router (Next.js 13+) ile SEO kurulumu hiç olmadığı kadar sistematik. Metadata API, sitemap.ts, robots.ts ve server component’ler doğru kullanıldığında ortaya crawler’ların bayıldığı bir site çıkıyor.
Bu rehberde 2026 itibarıyla eksiksiz bir Next.js SEO kurulumunu adım adım gösteriyoruz: rendering kararından JSON-LD’ye, Core Web Vitals’tan AI crawler uyumuna kadar.
Hızlı özet: Next.js SEO üç karara dayanır: doğru rendering stratejisi (mümkünse SSG/ISR), Metadata API ile eksiksiz title/description/OG ve sitemap.ts + robots.ts + JSON-LD üçlüsü. Client-side only render, çift H1 ve unutulan canonical en sık üç hatadır. AI crawler’lar JavaScript çalıştırmadığından sunucudan dönen HTML belirleyicidir.
Rendering Stratejisi: SEO’nun Yüzde 50’si Bu Kararda
Kural basit: içerik kullanıcıya göre değişmiyorsa statik üret. Next.js’te bir sayfanın SEO performansını en çok etkileyen şey, o sayfanın nasıl render edildiğidir. Metadata’dan da, schema’dan da önce gelir.
App Router’da elinizde dört seçenek var:
| Strateji | Nasıl çalışır | TTFB | SEO etkisi | Ne zaman kullanın |
|---|---|---|---|---|
| SSG (statik) | Build’de bir kez üretilir | En düşük (<100ms) | Mükemmel: tam HTML, anında yanıt | Blog, hizmet sayfası, landing |
| ISR | Statik + revalidate ile arka planda yenileme | Düşük | Mükemmel: taze içerik + statik hız | Ürün sayfası, haber, fiyat listesi |
| SSR (dynamic) | Her istekte sunucuda render | Orta-yüksek (300-800ms) | İyi ama TTFB riski | Kişiye özel veya gerçek zamanlı içerik |
| CSR (client-only) | Tarayıcıda JS ile render | Kabuk hızlı, içerik geç | Zayıf: botlar boş sayfa görebilir | Dashboard, panel; asla halka açık içerik |
Pratikte pazarlama sitelerinin %90’ı SSG + ISR karışımıyla çözülür. Bir e-ticaret ürün sayfasında export const revalidate = 3600 demek, saatte bir tazelenen ama her istekte statik hızda dönen bir sayfa demektir. SSR’ı ancak “bu içerik gerçekten her istekte farklı mı?” sorusuna evet diyebiliyorsanız seçin.
Kendi projelerimizde ölçtüğümüz fark net: aynı içeriğin SSR’dan SSG’ye alınması TTFB’yi tipik olarak 400-600ms’den 50-100ms bandına indiriyor. Bu doğrudan Core Web Vitals skorlarınıza yansıyor.
Metadata API: title, description ve OG Kurulumu
App Router’da <Head> bileşeni yok; onun yerine her layout.tsx ve page.tsx dosyasından metadata objesi veya generateMetadata fonksiyonu export edersiniz. Next.js bunları birleştirip <head> içine basar.
Kök layout’ta temel kurulum şöyle görünür:
// app/layout.tsx
import type { Metadata } from "next";
export const metadata: Metadata = {
metadataBase: new URL("https://siteniz.com"),
title: {
default: "Siteniz — Ana Değer Önermesi",
template: "%s | Siteniz",
},
description: "140-155 karakterlik, tam cümle site açıklaması.",
openGraph: {
type: "website",
locale: "tr_TR",
siteName: "Siteniz",
},
};
İki kritik detay:
metadataBaseşart. Bunu tanımlamazsanız OG image ve canonical gibi göreli URL’ler yanlış üretilir; Next.js build’de uyarı basar ama site çalışmaya devam eder, hata sessizce canlıya gider.title.templatesayesinde alt sayfalar sadece kendi başlığını verir, marka eki otomatik eklenir. Çift marka eki (“Sayfa | Marka | Marka”) sorununu kökten çözer.
Dinamik sayfalarda ise generateMetadata kullanırsınız:
// app/blog/[slug]/page.tsx
export async function generateMetadata({ params }): Promise<Metadata> {
const post = await getPost(params.slug);
return {
title: post.title, // 50-60 karakter hedefleyin
description: post.excerpt, // 140-155 karakter, kesik olmasın
alternates: { canonical: `/blog/${params.slug}/` },
openGraph: {
title: post.title,
description: post.excerpt,
images: [{ url: post.ogImage, width: 1200, height: 630 }],
},
};
}
Burada alternates.canonical satırına dikkat edin. Next.js canonical’ı sizin yerinize yazmaz; unutulursa sayfa canonical’sız yayınlanır ve UTM’li, parametreli kopyalar Google’da ayrı URL olarak birikir. Denetlediğimiz Next.js sitelerinde en sık rastladığımız eksik bu.
generateStaticParams: Dinamik Sayfaları Statik Yapmak
generateStaticParams, dinamik route’ların ([slug]) hangi sayfalara açılacağını build zamanında söyler; böylece o sayfalar SSR yerine statik üretilir. SEO açısından bu, “dinamik URL yapısı + statik hız” demek: iki dünyanın en iyisi.
// app/blog/[slug]/page.tsx
export async function generateStaticParams() {
const posts = await getAllPosts();
return posts.map((post) => ({ slug: post.slug }));
}
Bununla birlikte iki ayar daha önemli:
export const dynamicParams = falsederseniz listede olmayan slug’lar 404 döner. Sonradan eklenen içerik içintruebırakıp ISR ile birleştirmek genelde daha doğru.- 500+ sayfalık sitelerde hepsini build’de üretmek süreyi şişirir. En çok trafik alan sayfaları
generateStaticParams’ta üretip kalanını ilk istekte ISR’a bırakmak dengeli bir çözüm.
sitemap.ts ve robots.ts: Kod Olarak Crawl Yönetimi
App Router’da sitemap ve robots birer TypeScript dosyasıdır; elle XML yazmazsınız. app/sitemap.ts build’de /sitemap.xml’e, app/robots.ts ise /robots.txt’ye dönüşür.
// app/sitemap.ts
import type { MetadataRoute } from "next";
export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const posts = await getAllPosts();
return [
{ url: "https://siteniz.com/", lastModified: new Date(), priority: 1 },
...posts.map((p) => ({
url: `https://siteniz.com/blog/${p.slug}/`,
lastModified: p.updatedAt,
})),
];
}
lastModified alanını CMS’teki gerçek güncelleme tarihinden beslemek önemli; Google bu sinyali yeniden tarama önceliği için kullanıyor. Rastgele new Date() basmak “her şey bugün değişti” demek, sinyali çöpe çevirir.
// app/robots.ts
import type { MetadataRoute } from "next";
export default function robots(): MetadataRoute.Robots {
return {
rules: [
{ userAgent: "*", allow: "/", disallow: ["/api/", "/panel/"] },
{ userAgent: "GPTBot", allow: "/" },
{ userAgent: "PerplexityBot", allow: "/" },
],
sitemap: "https://siteniz.com/sitemap.xml",
};
}
Bir tuzak: bota özel bir grup açtığınızda o bot artık * grubundaki kuralları okumaz. GPTBot’a özel grup açıp disallow satırlarını tekrarlamazsanız, genel kısıtlarınız o bot için geçersiz kalır. Crawl kurgusunun tamamı için teknik SEO checklist’imize bakabilirsiniz.
JSON-LD: Schema’yı Server Component’te Basın
Next.js’te JSON-LD eklemenin tek doğru yolu, schema’yı sunucuda üretip ilk HTML’in içine gömmektir:
// app/blog/[slug]/page.tsx (server component)
export default async function Page({ params }) {
const post = await getPost(params.slug);
const jsonLd = {
"@context": "https://schema.org",
"@type": "Article",
headline: post.title,
datePublished: post.publishedAt,
author: { "@type": "Person", name: "Abdullah Üstün" },
};
return (
<article>
<script
type="application/ld+json"
dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }}
/>
{/* içerik */}
</article>
);
}
dangerouslySetInnerHTML burada gerçekten tehlikeli değil; veri sizin kontrolünüzde ve JSON.stringify’dan geçiyor. Asıl tehlikeli olan, schema’yı client component’te useEffect ile enjekte etmek: JavaScript çalıştırmayan botlar (AI crawler’ların tamamı dahil) o schema’yı hiç görmez. Hangi sayfa tipine hangi schema’nın uyduğunu schema markup rehberinde detaylandırdık.
next/image ile Core Web Vitals
next/image doğru kullanıldığında LCP ve CLS’in büyük kısmını kendiliğinden çözer: otomatik WebP/AVIF dönüşümü, boyut bazlı srcset, lazy loading ve layout shift önleme hepsi paket dahilinde.
SEO açısından üç kural yeter:
- Hero görseline
priorityverin. Ekranın üstündeki LCP görseli lazy load edilirse LCP kolayca 1 saniye uzar.priorityprop’u preload ekler; hedefiniz LCP <1.5s olmalı. width/height(veyafill+ oranlı kap) her zaman tanımlı olsun. CLS’in bir numaralı sebebi boyutsuz görseldir.sizesprop’unu gerçekçi yazın. Yazmazsanız tarayıcı masaüstünde de tam genişlik varsayar ve mobil kullanıcıya gereksiz büyük dosya iner.
Bir de alt metni: next/image alt’ı zorunlu tutar ama “resim1” yazan alt SEO’ya hiçbir şey katmaz. Görseli tarif eden tam bir cümle yazın.
Sık Yapılan 7 Next.js SEO Hatası
Denetlediğimiz Next.js projelerinde tekrar tekrar aynı hatalarla karşılaşıyoruz:
- Client-side only render. Sayfanın ana içeriği
"use client"+ fetch ile tarayıcıda çekiliyor. Googlebot render kuyruğuyla belki görür, AI botlar hiç görmez. İçerik server component’te kalmalı. - Çift H1. Layout’ta logo H1, sayfada başlık H1. Her sayfada tek H1 olmalı; logoyu
divveyapyapın. - Canonical unutulmuş.
alternates.canonicalyazılmadığı için parametreli kopyalar indekse sızıyor. metadataBasetanımsız. OG image URL’lerilocalhost:3000olarak canlıya çıkıyor. Evet, gerçekten görüyoruz bunu.- 404 yerine 200 dönen soft-404. Bulunamayan slug’da
notFound()çağırmak yerine “İçerik bulunamadı” yazısıyla 200 dönmek. Google bunları düşük kaliteli sayfa olarak biriktirir. loading.tsxyokken ağır SSR. Yavaş veri kaynağına bağlı SSR sayfası, streaming olmadan TTFB’yi 1s+ seviyeye taşır.- Sitemap’te silinen sayfalar. CMS’ten silinen içerik sitemap’ten düşürülmüyor, Search Console 404 uyarılarıyla doluyor.
sitemap.tsher build’de güncel listeyi çekmeli.
Bu yedi maddenin ortak teması şu: Next.js size güçlü araçlar veriyor ama hiçbirini sizin yerinize açmıyor.
AI Crawler’lar Neden Next.js Çıktısını Önemsiyor?
2026 itibarıyla GPTBot, ClaudeBot ve PerplexityBot JavaScript çalıştırmıyor; sunucunuzun döndürdüğü ilk HTML neyse, AI’ın gördüğü site odur. ChatGPT’nin veya Perplexity’nin sizi kaynak gösterip göstermeyeceği, büyük ölçüde bu ilk yanıtta belirleniyor.
Bu, rendering kararını daha da kritik hale getiriyor:
- SSG/ISR sayfalar AI botlar için ideal: tam içerikli HTML, düşük TTFB, timeout riski yok.
- SSR sayfalar çalışır ama yavaş veri kaynağında bot timeout’a düşebilir; AI crawler’lar klasik botlardan daha sabırsız.
- CSR sayfalar AI için pratikte görünmezdir.
Kontrol yöntemi basit: terminalden curl https://siteniz.com/sayfa/ | grep "aradığınız metin" çalıştırın. Metin ham HTML’de varsa AI botlar okuyabiliyor demektir. Yoksa o içerik yalnızca JavaScript sonrası oluşuyor ve AI görünürlüğünüz sıfıra yakın.
Bir adım ötesi, siteye llms.txt eklemek ve alıntılanabilir içerik yapısı kurmak; bu konunun tamamını GEO tarafında ayrıca ele alıyoruz. AI botların sitenize gerçekten erişip erişemediğini de varsaymayın: robots.txt “Allow” dese bile WAF katmanı botu 403/429 ile çevirebiliyor, sunucu loglarından doğrulayın.
Kurulum Kontrol Listesi
Yayına çıkmadan önce hızlı bir tur:
- Her halka açık sayfa SSG veya ISR ile üretiliyor
- Kök layout’ta
metadataBase+title.templatetanımlı - Her sayfada benzersiz title (50-60 kr) ve description (140-155 kr)
-
alternates.canonicaltüm indexlenebilir sayfalarda var -
sitemap.tsgerçeklastModifiedile dinamik üretiliyor -
robots.ts’te AI botlar bilinçli şekilde yönetiliyor - JSON-LD server component’te, ilk HTML’in içinde
- LCP görselinde
priority, tüm görsellerde boyut +sizes - Tek H1,
notFound()ile gerçek 404 -
curltesti: içerik ham HTML’de görünüyor
Bu listeyi geçen bir Next.js sitesi, hem Google’da hem AI arama yüzeylerinde teknik olarak hazırdır. Gerisi içerik kalitesi ve otorite meselesi.
Next.js Siteniz İçin Profesyonel Destek
Next.js projeniz var ama organik trafik beklediğiniz yerde değilse, sorun çoğu zaman bu rehberdeki maddelerin bir alt kümesidir; hangileri olduğunu ölçmeden bilemezsiniz. SEO hizmetimiz kapsamında Next.js sitelerine özel teknik denetim yapıyor, rendering stratejisinden metadata kurgusuna kadar tüm katmanları elden geçiriyoruz. Durumunuzu konuşmak için iletişim sayfasından ücretsiz 30 dakikalık görüşme planlayabilirsiniz.
Bu yazı 2026’da güncellendi. Next.js sürümleri ve crawler davranışları değiştikçe rehber güncelleniyor.
SSS