Next.js App Router'da Canonical Nasıl Eklenir?
Next.js App Router'da canonical, Metadata API içindeki alternates.canonical alanıyla üretilir. Intent'in kullandığı sürüm next@16.2.10; bu sürümde dinamik route parametreleri Promise olarak alınabilir. Canonical'ı root layout'ta tek bir sabit değer yapmak, alt sayfaları yanlışlıkla ana sayfaya canonicalize edebilir.
Statik sayfa
import type { Metadata } from "next";
export const metadata: Metadata = {
title: "Teknik SEO Rehberi | Intent",
description: "Teknik SEO sorunlarını teşhis etmek için uygulama rehberi.",
alternates: {
canonical: "/hub/rehberler/teknik-seo",
},
};
Next.js Metadata API dokümanı statik metadata nesnesi ile dinamik generateMetadata fonksiyonunun ayrı kullanım amaçlarını gösterir. Statik sayfa için sabit relative canonical yeterlidir.
Intent'teki dinamik blog route'u
Bu repository'deki gerçek route src/app/blog/[slug]/page.tsx içinde generateMetadata kullanır. Aynı veri kaynağıyla sadeleştirilmiş karşılığı:
import type { Metadata } from "next";
import { getPostBySlug } from "@/lib/blog";
export async function generateMetadata(
{ params }: { params: Promise<{ slug: string }> }
): Promise<Metadata> {
const { slug } = await params;
const post = getPostBySlug(slug);
return {
title: `${post.title} | Intent Blog`,
description: post.description,
alternates: {
canonical: `/blog/${post.slug}`,
},
};
}
Buradaki post.slug, ham query veya eski slug değil, içerik kaynağındaki normalize edilmiş değerdir. Veri bulunmuyorsa route 404 döndürmelidir; olmayan bir kaynağa canonical üretmek yerine notFound() kullanmak daha temizdir.
metadataBase ve host edge case'leri
Intent'in root layout'ında:
export const metadata: Metadata = {
metadataBase: new URL("https://intent.com.tr"),
};
Bu sayede canonical: "/blog/ornek" değeri production HTML'sinde https://intent.com.tr/blog/ornek olur. Relative URL'lerin metadataBase ile birleştirilmesi Next.js dokümantasyonunda açıklanır.
| Durum | Risk | Kontrol |
|---|---|---|
| localhost:3000 | Test host'unun canonical'a sızması | Build HTML'sinde localhost ara |
| Vercel preview | Preview URL'nin production yerine çıkması | Preview response head'ini incele |
| www / non-www | Aynı içeriğin iki hostta üretilmesi | Redirect ve canonical politikasını eşleştir |
| HTTP / HTTPS | Güvenli olmayan adresin sinyal olması | HTTPS canonical bekle |
| ?utm_source=... | Analytics parametresinin canonical'a taşınması | Canonical path'in query içermediğini doğrula |
| trailing slash | İki path biçiminin ayrışması | Tek slash politikasını kullan |
Altı katmanlı metadata verification
“View source'ta gördüm” tek başına yeterli değildir:
- Source: Route metadata veya generateMetadata hangi değeri döndürüyor?
- Build: next build metadata üretirken hata veriyor mu?
- HTTP: Gerçek host hangi status, redirect ve header'ları döndürüyor?
- Server HTML: curl ile gelen HTML içinde canonical var mı?
- DevTools: Elements DOM'u client script sonrasında değişiyor mu?
- Search Console: URL Inspection Google-selected canonical için ne gösteriyor?
curl -sS -D - https://intent.com.tr/blog/ornek-yazi -o /tmp/intent-page.html
rg -oi '<link[^>]+rel="canonical"[^>]*>' /tmp/intent-page.html
rg -oi 'https?://[^" ]*localhost[^" ]*|https?://[^" ]*vercel.app[^" ]*' /tmp/intent-page.html
DOM inspection tarayıcının mevcut DOM'unu, curl ise sunucunun döndürdüğü ilk HTML'yi gösterir. Client-side canonical mutasyonuna güvenmeyin.
Repository regression kontrolü
PAGE="https://intent.com.tr/blog/google-selected-canonical-neden-farkli"
HTML="$(curl -fsSL "$PAGE")"
grep -qi 'rel="canonical"' <<< "$HTML"
grep -q 'https://intent.com.tr/blog/google-selected-canonical-neden-farkli' <<< "$HTML"
test "$(grep -o '<h1\b' <<< "$HTML" | wc -l)" -eq 1
Sonra sitemap loc değerini ve makale iç linklerini aynı path ile karşılaştırın. Bu kontrol canonical'ın doğru yazıldığını kanıtlar; Google'ın mutlaka onu seçeceğini kanıtlamaz.
Sık hatalar
- Root layout'a canonical: "/" koyup bütün alt route'ları ezmek.
- post.slug yerine ham query veya eski slug kullanmak.
- Sitemap'te query parametreli URL bulundurmak.
- Preview host'unu production metadataBase yerine geçirmek.
- Canonical değişikliğini içerik kopyasını çözmeden kullanmak.
Canonical doğru üretildiğinde URL tercih sinyali netleşir. İndeksleme için yine 200 yanıt, erişilebilir HTML, anlamlı iç bağlantılar ve bağımsız içerik gerekir. Sinyaller için Google-selected canonical teşhis matrisine bakın.