Canonical URL Tanımlama: functions.php ile Yinelenen İçerik Önleme
SEO dünyasında en sık karşılaşılan ama en az fark edilen sorunlardan biri yinelenen içeriktir. WordPress sitende aynı içeriğe birden fazla URL üzerinden ulaşılabiliyorsa, arama motorları hangi sayfanın “asıl” sayfa olduğunu bilemez ve bu durum sıralamalarını doğrudan etkiler. Canonical URL mekanizması tam da bu noktada devreye girer. Peki WordPress’in bunu zaten yönettiğini düşünüyorsanız, yanılıyor olabilirsiniz. Yoast veya Rank Math gibi eklentiler yüzeysel çözümler sunsa da gerçek kontrolü elde etmek için functions.php dosyasına inmek gerekir.
Canonical URL Nedir ve Neden Önemlidir
Canonical URL, bir sayfanın “tercih edilen” veya “yetkili” versiyonunu arama motorlarına bildiren HTML etiketidir. bölümüne şu şekilde eklenir:
<link rel="canonical" href="https://example.com/asil-sayfa/" />
Google bu etiketi gördüğünde, farklı URL’ler üzerinden erişilen aynı içeriği tek bir kaynak altında toplar. PageRank dağılmasını önler, tarama bütçesini (crawl budget) korur ve indeksleme kalitesini artırır.
WordPress sitelerinde yinelenen içerik genellikle şu durumlarda oluşur:
- www ve www olmayan versiyon:
www.site.comilesite.comaynı sayfayı sunar - HTTP ve HTTPS: Yönlendirme tam yapılandırılmamışsa ikisi de erişilebilir olabilir
- Sayfalama:
/sayfa/1/ve/aynı içeriği gösterebilir - WooCommerce ürün varyasyonları:
?color=red&size=Lgibi parametre kombinasyonları - Arşiv sayfaları: Kategori, etiket, tarih arşivleri aynı yazıları listeler
- Print versiyonlar:
?print=1parametresi - Session ID’leri: Bazı cache eklentileri veya üyelik sistemleri ekler
WordPress’in Varsayılan Canonical Davranışı
WordPress 4.4 sürümünden itibaren wp_head() hook’u üzerinden otomatik canonical etiketleri ekler. Ancak bu davranış her zaman istediğiniz gibi çalışmaz. Özellikle şu senaryolarda yetersiz kalır:
- WooCommerce filtreleme parametreleri (
?min_price=100&max_price=500) - Özel post type’larınızda yapılandırma eksikliği
- Sayfalama mantığında yanlış URL üretimi
- Çoklu domain yapılarında (staging, CDN, subdomains)
Bunu test etmek için tarayıcı geliştirici araçlarında sayfa kaynağını inceleyin. içinde rel="canonical" araması yapın. Gördüğünüz URL beklediğinizle örtüşüyor mu? Çoğu zaman hayır.
functions.php’ye Neden İniyoruz
Eklenti bağımlılığını azaltmak, siteniz üzerinde tam kontrol sahibi olmak ve özelleştirilmiş iş mantığı uygulamak için functions.php ideal yerdir. Child theme kullanıyorsanız (ki kullanıyor olmalısınız), bu dosya güncellemelerden etkilenmez ve değişiklikleriniz güvende kalır.
Başlamadan önce şunu netleştirelim: Eğer Yoast SEO veya Rank Math kullanıyorsanız, bu eklentiler wp_head üzerinden canonical çıktısı üretir. Sizin kod yazdığınız fonksiyonlar çakışabilir. Ya eklentiyi bu konuda devre dışı bırakmanız gerekir ya da öncelik (priority) sıralamasını dikkatli yönetmeniz gerekir.
Temel Canonical Hook’u Anlamak
WordPress canonical URL yönetimi için iki ana hook kullanır:
get_canonical_url: Filtreleme hook’u, canonical URL string’ini döndürürwp_head: Action hook’u,içine çıktı üretirrel_canonical: Canonical etiketinin çıktısını tamamen kontrol etmenizi sağlar
Önce mevcut canonical davranışını devre dışı bırakıp kendi fonksiyonumuzu yazalım:
<?php
// Varsayılan WordPress canonical çıktısını kaldır
remove_action('wp_head', 'rel_canonical');
// Kendi canonical fonksiyonumuzu ekle
add_action('wp_head', 'custom_canonical_url', 1);
function custom_canonical_url() {
global $wp_the_query;
if (!is_singular() && !is_front_page()) {
return;
}
$canonical = get_permalink($wp_the_query->get_queried_object_id());
if ($canonical) {
echo '<link rel="canonical" href="' . esc_url($canonical) . '" />' . "n";
}
}
Bu temel yapıyla singular sayfalar için canonical tanımlıyoruz. Ama iş burada bitmiyor.
URL Parametrelerini Temizleme
En kritik senaryo, URL parametrelerinin canonical URL’e dahil edilmemesidir. UTM parametreleri, filtreler, session ID’leri canonical URL’e girmemelidir.
<?php
function get_clean_canonical_url() {
global $wp;
// Mevcut isteğin base URL'ini al
$current_url = home_url(add_query_arg(array(), $wp->request));
// Temizlenmesi gereken parametreler
$ignored_params = array(
'utm_source',
'utm_medium',
'utm_campaign',
'utm_term',
'utm_content',
'fbclid',
'gclid',
'ref',
'source',
'print',
'sid',
'session_id',
'PHPSESSID',
);
$parsed = parse_url($current_url);
if (isset($parsed['query'])) {
parse_str($parsed['query'], $params);
foreach ($ignored_params as $param) {
unset($params[$param]);
}
$clean_query = http_build_query($params);
$canonical = $parsed['scheme'] . '://' . $parsed['host'];
if (isset($parsed['path'])) {
$canonical .= $parsed['path'];
}
if (!empty($clean_query)) {
$canonical .= '?' . $clean_query;
}
} else {
$canonical = $current_url;
}
return trailingslashit($canonical);
}
Bu fonksiyon, UTM parametrelerini ve diğer “gürültülü” parametreleri canonical URL’den temizler. Sosyal medyadan gelen fbclid veya Google Ads’dan gelen gclid parametreleri sıklıkla sorun yaratır.
WooCommerce için Özel Canonical Yönetimi
WooCommerce sitelerinde durum çok daha karmaşıklaşır. Ürün filtreleme, sıralama parametreleri ve varyasyon URL’leri yönetilmezse canonical kargaşası kaçınılmazdır.
<?php
add_filter('get_canonical_url', 'woocommerce_canonical_fix', 10, 2);
function woocommerce_canonical_fix($canonical, $post) {
if (!class_exists('WooCommerce')) {
return $canonical;
}
// WooCommerce mağaza sayfası
if (is_shop()) {
$shop_page_id = wc_get_page_id('shop');
return get_permalink($shop_page_id);
}
// Ürün kategori sayfaları - sayfalama dahil
if (is_product_category()) {
$term = get_queried_object();
$base_url = get_term_link($term);
// Sayfalama varsa canonical'a dahil et
$paged = get_query_var('paged');
if ($paged > 1) {
return trailingslashit($base_url) . 'page/' . $paged . '/';
}
return $base_url;
}
// Tekil ürün sayfaları - varyasyon parametrelerini temizle
if (is_product()) {
// ?attribute_pa_renk=mavi gibi parametreleri canonical'a ekleme
return get_permalink($post->ID);
}
return $canonical;
}
Burada dikkat edilmesi gereken nokta: WooCommerce’in kendi canonical filtresi vardır ve bunun üzerine yazıyoruz. priority değerini 10’dan yüksek tutarsanız WooCommerce’in kendi mantığı override edilecektir.
Sayfalama için Canonical Yönetimi
Sayfalama hem blog listesi hem de ürün arşiv sayfalarında önemli bir sorun kaynağıdır. İlk sayfa ile /page/1/ aynı içeriği sunar:
<?php
add_action('wp_head', 'fix_pagination_canonical', 1);
function fix_pagination_canonical() {
// Sadece arşiv, kategori, etiket ve ana blog sayfalarında çalış
if (!is_archive() && !is_home() && !is_search()) {
return;
}
global $wp_query;
$paged = get_query_var('paged');
$current_page = max(1, $paged);
if (is_home()) {
$base = get_option('page_for_posts')
? get_permalink(get_option('page_for_posts'))
: home_url('/');
} elseif (is_category()) {
$base = get_category_link(get_queried_object_id());
} elseif (is_tag()) {
$base = get_tag_link(get_queried_object_id());
} elseif (is_author()) {
$base = get_author_posts_url(get_queried_object_id());
} else {
$base = get_pagenum_link(1);
}
if ($current_page > 1) {
$canonical = trailingslashit($base) . 'page/' . $current_page . '/';
} else {
$canonical = $base;
}
// WordPress'in kendi canonical çıktısını devre dışı bırak
remove_action('wp_head', 'rel_canonical');
echo '<link rel="canonical" href="' . esc_url($canonical) . '" />' . "n";
}
Özel Post Type’lar için Canonical
Eğer custom post type’larınız varsa, bunlar için de özel canonical mantığı kurmanız gerekebilir. Diyelim ki “portfolio” adında bir CPT’niz var ve hem /portfolio/proje-adi/ hem de /proje-adi/ URL’si çalışıyor:
<?php
add_filter('get_canonical_url', 'cpt_canonical_fix', 20, 2);
function cpt_canonical_fix($canonical, $post) {
$custom_post_types = array('portfolio', 'urun-dokuman', 'vaka-calismasi');
if (!in_array($post->post_type, $custom_post_types)) {
return $canonical;
}
// Her zaman özel post type'ın kendi permalink'ini kullan
$correct_permalink = get_permalink($post->ID);
// URL protokolünü site ayarına göre normalize et
$site_url = get_option('siteurl');
$protocol = strpos($site_url, 'https') === 0 ? 'https' : 'http';
$correct_permalink = preg_replace('/^https?/', $protocol, $correct_permalink);
return $correct_permalink;
}
Canonical ile Birlikte Kullanılması Gereken: Hreflang
Çok dilli siteler için canonical ve hreflang birlikte çalışmalıdır. WPML veya Polylang kullanıyorsanız şu yapıyı kurun:
<?php
add_action('wp_head', 'multilingual_canonical_and_hreflang', 1);
function multilingual_canonical_and_hreflang() {
if (!function_exists('pll_get_post') && !defined('ICL_SITEPRESS_VERSION')) {
return;
}
global $post;
if (!is_singular() || !$post) {
return;
}
// Mevcut dilin canonical'ı
$canonical = get_permalink($post->ID);
remove_action('wp_head', 'rel_canonical');
echo '<link rel="canonical" href="' . esc_url($canonical) . '" />' . "n";
// Polylang kullanıyorsa hreflang ekle
if (function_exists('pll_the_languages')) {
$languages = pll_the_languages(array('raw' => 1));
foreach ($languages as $lang) {
if (!empty($lang['url'])) {
$post_translation = pll_get_post($post->ID, $lang['slug']);
if ($post_translation) {
$translation_url = get_permalink($post_translation);
echo '<link rel="alternate" hreflang="' . esc_attr($lang['locale']) . '" href="' . esc_url($translation_url) . '" />' . "n";
}
}
}
// x-default hreflang
$default_lang_post = pll_get_post($post->ID, pll_default_language());
if ($default_lang_post) {
echo '<link rel="alternate" hreflang="x-default" href="' . esc_url(get_permalink($default_lang_post)) . '" />' . "n";
}
}
}
Canonical URL Doğrulama Fonksiyonu
Yazdığınız canonical mantığının doğru çalışıp çalışmadığını test etmek için admin toolbar’a veya bir shortcode’a debug çıktısı ekleyebilirsiniz:
<?php
// Sadece geliştirme ortamında kullan
add_action('wp_footer', 'debug_canonical_url');
function debug_canonical_url() {
// Sadece admin kullanıcılar görsün ve sadece dev ortamında çalışsın
if (!current_user_can('manage_options') || !WP_DEBUG) {
return;
}
global $wp_query;
$object = $wp_query->get_queried_object();
$debug_info = array();
// WordPress'in belirlediği canonical
if ($object instanceof WP_Post) {
$debug_info['wp_canonical'] = get_permalink($object->ID);
$debug_info['post_type'] = $object->post_type;
$debug_info['post_id'] = $object->ID;
}
$debug_info['current_url'] = (is_ssl() ? 'https' : 'http') . '://' . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI'];
$debug_info['is_singular'] = is_singular() ? 'evet' : 'hayir';
$debug_info['is_archive'] = is_archive() ? 'evet' : 'hayir';
$debug_info['paged'] = get_query_var('paged');
echo '<script>console.group("Canonical Debug");';
foreach ($debug_info as $key => $value) {
echo 'console.log("' . esc_js($key) . ':", "' . esc_js($value) . '");';
}
echo 'console.groupEnd();</script>';
}
Bu fonksiyon, tarayıcı konsoluna canonical bilgilerini yazdırır. Sitenin farklı sayfalarında gezinerek canonical URL’lerin doğru üretilip üretilmediğini kontrol edebilirsiniz.
Sık Karşılaşılan Hatalar ve Çözümleri
Çift canonical etiketi: En yaygın sorun budur. Hem WordPress’in varsayılan canonical’ı hem de sizin fonksiyonunuz aynı anda çıktı üretiyordur. Çözüm: remove_action('wp_head', 'rel_canonical') satırını her zaman ekleyin.
HTTPS/HTTP karışıklığı: Bazen get_permalink() HTTP döndürürken siteniz HTTPS üzerinde çalışıyor. Bunu önlemek için:
- Permalink’i aldıktan sonra
set_url_scheme($url, 'https')fonksiyonunu kullanın - WordPress’in
siteurlseçeneğinin HTTPS ile başladığından emin olun
Trailing slash tutarsızlığı: Bazı URL’ler / ile biterken bazıları bitmez. WordPress admin panelinde “Ayarlar > Kalıcı Bağlantılar” yapılandırmanızla tutarlı olun. trailingslashit() veya untrailingslashit() fonksiyonlarını tutarlı biçimde kullanın.
WooCommerce filtresi ile çakışma: WooCommerce kendi canonical filtrelerini çok düşük öncelikle (priority 10) ekler. Sizin filtre önceliğinizi 20 veya daha yüksek yaparsanız WooCommerce’in yazdıklarının üzerine yazarsınız.
Staging ortamı sızması: Staging siteniz production canonical URL’ini kullanmalı, kendi URL’ini değil. Bunu yönetmek için:
<?php
add_filter('get_canonical_url', 'force_production_canonical', 99);
function force_production_canonical($canonical) {
$staging_domain = 'staging.siteniz.com';
$production_domain = 'www.siteniz.com';
// Canonical URL'deki staging domain'i production ile değiştir
$canonical = str_replace($staging_domain, $production_domain, $canonical);
// HTTP'yi HTTPS'e çevir
$canonical = str_replace('http://' . $production_domain, 'https://' . $production_domain, $canonical);
return $canonical;
}
Bu kodu sadece staging ortamında aktif etmek için wp-config.php içinde bir sabit tanımlayabilirsiniz.
Canonical URL’leri Düzenli Test Etme
Tüm bu kodları yazdıktan sonra düzenli test rutini oluşturmak şarttır. Sysadmin olarak şunu söyleyeyim: Yazan kişi kontrolü yapmazsa yanlış giden şeyi fark eden olmaz.
Test etmeniz gereken senaryolar:
- Ana sayfa (
/) için canonical - Blog listesi sayfası ve sayfalı versiyonları (
/blog/page/2/) - Tekil yazı/sayfa canonical’ları
- Kategori ve etiket arşivleri
- WooCommerce mağaza sayfası ve filtrelenmiş versiyonları
- Ürün sayfaları ve varyasyon URL’leri
- Arama sonuçları sayfası (
?s=kelime) - 404 sayfaları (canonical olmamalı)
Google Search Console’un “URL İnceleme” aracı, herhangi bir URL için canonical etiketini doğrudan gösterir. Screaming Frog gibi araçlar ise toplu tarama yaparak tüm siteyi analiz eder.
Canonical URL konfigürasyonu bir kez yapılıp unutulacak bir şey değildir. Yeni özellik eklendikçe, tema değiştirildikçe veya WooCommerce güncellemelerinden sonra mutlaka kontrol edilmelidir.
Sonuç
functions.php üzerinden canonical URL yönetimi, SEO’nun en teknik ama en etkili alanlarından biridir. Eklentilere tamamen bıraktığınızda kontrolü kaybedersiniz; kendi kodunuzu yazdığınızda ise tam olarak ne olduğunu bilirsiniz.
Bu yazıda anlattıkları uygulamak için önerilen sıra şudur: Önce mevcut siteyi Screaming Frog ile tarayın ve canonical sorunlarını listeleyin. Sonra child theme’inizin functions.php dosyasına adım adım ekleyin, her adımda tarayıcı konsolunu ve sayfa kaynağını kontrol edin. WooCommerce kullanıyorsanız mağaza, kategori ve ürün sayfalarını ayrı ayrı test edin. Son olarak Google Search Console’da URL İnceleme aracını kullanarak kritik sayfaları doğrulayın.
Canonical URL yönetimini doğru kurguladığınızda arama motoru tarama bütçenizi korumuş, PageRank dağılmasını önlemiş ve indeksleme kalitesini artırmış olursunuz. Bu üç faktör uzun vadede organik trafiğe doğrudan yansır. Teknik SEO, sysadmin işidir; ve iyi bir sysadmin altyapıyı sağlam kurar.
