# Yapısal Veri Hatası: Aynı Sayfada İki Şema Olursa Ne Olur?

> Üç ürün sayfamızda aynı türden iki ayrı şema basılıyordu. İkisi de geçerliydi, ikisi de doğrulamadan geçiyordu, ama farklı bilgi söylüyorlardı.

- Kanonik URL: https://algoritmaajans.com/tr/blog/yapisal-veri-hatasi-ayni-sayfada-iki-sema
- Dil: tr
- Kategori: Teknoloji
- Yayın: 2026-08-27
- Güncelleme: 2026-08-29
- Yayıncı: Algoritma Ajans

---

**Aynı sayfada aynı türden iki şema bulunduğunda Google hangisinin doğru olduğunu bilemez ve genelde ikisini birden yok sayar. Sonuç hata mesajı değil, sessizce kaybolan zengin sonuçlardır — bu yüzden aylarca fark edilmez.**

Yapısal veri (schema markup) eklemek iyi bir fikirdir. Fazladan eklemek değildir.

Kendi ürün sayfalarımızı denetlerken şunu bulduk: üç sayfada **aynı türden iki ayrı şema** basılıyordu. İkisi de geçerliydi, ikisi de doğrulama araçlarından geçiyordu, ama birbirinden farklı bilgi söylüyorlardı.

## Nasıl oluşuyor

Next.js'te yapısal veri iki yerden gelebilir:

**Layout dosyasından** — tüm alt sayfalara uygulanır:

```tsx
// app/urunler/[slug]/layout.tsx
<ProductJsonLd slug={slug} locale={locale} />
```

**Sayfa dosyasından** — o sayfaya özel:

```tsx
// app/urunler/[slug]/page.tsx
const jsonLd = {
  "@context": "https://schema.org",
  "@type": "SoftwareApplication",
  name: "Nakliyat Yazılımı",
  // ...
};

<script type="application/ld+json"
  dangerouslySetInnerHTML={{ __html: JSON.stringify(jsonLd) }} />
```

Her ikisi de tek başına doğru. Birlikte olduklarında sayfada iki `SoftwareApplication` nesnesi bulunur.

Genelde şöyle oluyor: sayfa yazılırken şema doğrudan sayfaya konuyor. Sonra proje büyüyünce şema üretimi ortak bir bileşene taşınıyor ve layout'a bağlanıyor. Ama eski satır silinmiyor.

Aynı hatanın üç varyantı daha var ve hepsini sahada gördük:

**Eklenti üstüne eklenti.** WordPress'te bir SEO eklentisi `Article` üretir, tema da kendi `Article`'ını basar. İkisi de "ben hallederim" varsayar.

**Çerez / analitik enjeksiyonu.** Bazı üçüncü parti araçlar sayfaya kendi `Organization` şemasını ekler. Sizinkiyle çakışır ve genelde eksiktir.

**Kopyala-yapıştır sayfa.** Bir ürün sayfasını çoğaltıp içeriği değiştirirsiniz, ama şemadaki `name` alanı eski üründe kalır. Bu çakışma değil ama daha kötüsü: iki farklı sayfa Google'a aynı ürünü anlatır.

## Neden sorun

Google aynı sayfada aynı türden birden çok varlık bulduğunda hangisinin doğru olduğunu bilmez.

- **Fiyat farklıysa** hangisini gösterecek?
- **Puan farklıysa** hangisine göre yıldız basacak?
- **Ad farklıysa** hangisini varlık olarak kaydedecek?

![Tek bir kutunun üzerinde 29,99 ve 19,95 yazan iki ayrı fiyat etiketi — aynı sayfadaki iki şemanın birbiriyle çelişmesi](/images/blog/cakisan-yapisal-veri-inline-1.jpg)

Google'ın yapısal veri belgeleri bu konuda net: bir sayfa bir ana varlığı tanımlamalıdır. Çelişki bulduğunda Google genelde en güvenli yolu seçer — **hiçbirini kullanmaz.** Zengin sonuç kaybı hata mesajıyla gelmez, sadece görünmez olursunuz.

Bizim durumumuzda iki şemadan biri fiyat bilgisi içermiyordu, diğeri `offers` alanı taşıyordu. Yani Google'a aynı ürün için hem "fiyatı var" hem "yok" diyorduk.

İkinci bir maliyet daha var ve bu artık daha da önemli: yapay zeka motorları da bu veriyi okuyor. Bir model "şu ürünün fiyatı ne" sorusuna cevap üretirken çelişkili iki değer bulursa ya ikisini de kullanmaz ya da yanlış olanı seçer. İkisi de sizin aleyhinize. [ChatGPT'nin sitenizi neden okumadığını](/tr/blog/chatgpt-sitenizi-neden-okumuyor-kontrol-listesi) sıraladığımız listede altıncı madde tam olarak bu.

## Nasıl tespit edilir

En hızlı yol tarayıcı konsolu:

```js
document.querySelectorAll('script[type="application/ld+json"]').length
```

Birden fazla `<script>` olması sorun değil — sorun aynı `@type`'ın tekrarlanması. Türleri saymak için:

```js
[...document.querySelectorAll('script[type="application/ld+json"]')]
  .flatMap(s => {
    const d = JSON.parse(s.textContent);
    return d["@graph"] ? d["@graph"] : [d];
  })
  .map(x => x["@type"])
  .reduce((acc, t) => (acc[t] = (acc[t] || 0) + 1, acc), {});
```

Beklenen çıktı şuna benzemeli:

```
{ SoftwareApplication: 1, BreadcrumbList: 1, FAQPage: 1, Organization: 1 }
```

Herhangi bir değer 1'den büyükse orada bir çakışma var.

Terminalden toplu kontrol için:

```bash
curl -s https://siteniz.com/urun-sayfasi \
  | grep -o '"@type":"[A-Za-z]*"' | sort | uniq -c
```

Sitenin tamamını taramak isterseniz sitemap üzerinden dönün:

```bash
curl -s https://siteniz.com/sitemap.xml \
  | grep -oE 'https://[^<]+' \
  | while read u; do
      say=$(curl -s "$u" | grep -o '"@type":"Product"' | wc -l | tr -d ' ')
      [ "$say" -gt 1 ] && echo "$say  $u"
    done
```

Bu döngü, `Product` şeması birden fazla geçen sayfaları listeler. `Product` yerine kendi ana türünüzü yazın.

Üç doğrulama aracı da işinize yarar ama farklı şeyler söylerler:

| Araç | Ne söyler | Ne söylemez |
|---|---|---|
| Rich Results Test | Google'ın zengin sonuç için kullanabildiği türler | Çakışma olduğunu açıkça |
| Schema Markup Validator | schema.org'a göre sözdizimsel geçerlilik | Google'ın kullanıp kullanmayacağını |
| Search Console → Geliştirmeler | Sitedeki geçerli/hatalı öğe sayısı, zaman içinde | Tek tek hangi sayfada çakışma olduğunu |

Üçünü de kullanın ama karar merciini karıştırmayın: bir şema "geçerli" olabilir ve Google onu yine de kullanmayabilir.

## Doğru yapı: tek graph

Birden çok şemayı ayrı `<script>` etiketlerine dağıtmak yerine tek bir `@graph` içinde toplayın:

```json
{
  "@context": "https://schema.org",
  "@graph": [
    {
      "@type": "Organization",
      "@id": "https://siteniz.com/#kurum",
      "name": "Örnek Ajans",
      "url": "https://siteniz.com",
      "logo": { "@type": "ImageObject", "url": "https://siteniz.com/logo.png" }
    },
    {
      "@type": "SoftwareApplication",
      "@id": "https://siteniz.com/urunler/x#urun",
      "name": "Nakliyat Yazılımı",
      "applicationCategory": "BusinessApplication",
      "publisher": { "@id": "https://siteniz.com/#kurum" }
    },
    {
      "@type": "BreadcrumbList",
      "itemListElement": [
        { "@type": "ListItem", "position": 1, "name": "Ürünler", "item": "https://siteniz.com/urunler" },
        { "@type": "ListItem", "position": 2, "name": "Nakliyat Yazılımı" }
      ]
    },
    {
      "@type": "FAQPage",
      "mainEntity": []
    }
  ]
}
```

`@id` kullanmanın faydası: aynı kurumu her şemada tekrar tanımlamak yerine bir kez tanımlayıp diğerlerinden referans verirsiniz. Google varlıkları birbirine bağlar, siz de tekrar etmemiş olursunuz.

`@id` değerlerinde iki kural var. **Mutlak URL olsun** — göreli yol ya da rastgele bir kimlik, farklı sayfalardaki referansları birbirine bağlamaz. **Sabit kalsın** — kurum kimliğiniz her sayfada `https://siteniz.com/#kurum` ise Google tüm sayfalardaki referansları tek bir varlıkta toplar. Her sayfada farklı bir kimlik üretirseniz aynı kurumu yüzlerce ayrı varlık olarak görür.

Son maddedeki `BreadcrumbList`'in son öğesinde `item` alanının olmaması bilinçli: son basamak zaten bulunduğunuz sayfadır, kendine link vermesine gerek yok.

Tek üretim noktası olduğunda çakışma da mümkün olmaz.

## Hangi şema türü ne işe yarar

Çakışmayı önlemenin ilk adımı, sayfaya gerçekten hangi türün gerektiğini bilmek. Fazla şema eklemenin sebebi genelde "ne lazım" sorusunun cevaplanmamış olmasıdır.

| Tür | Hangi sayfada | Somut karşılığı |
|---|---|---|
| `Organization` | Site genelinde bir kez | Bilgi paneli, marka eşleştirme |
| `LocalBusiness` | İletişim / şube sayfası | Harita, çalışma saati, arama butonu |
| `Article` / `BlogPosting` | Blog yazıları | Tarih, yazar, haber görünürlüğü |
| `Product` | Ürün sayfası | Fiyat, stok, yıldız |
| `SoftwareApplication` | Yazılım ürünü sayfası | Kategori, platform, fiyat |
| `FAQPage` | SSS içeren sayfa | Sonuçta açılır soru listesi |
| `BreadcrumbList` | Tüm iç sayfalar | Sonuçta URL yerine kırılım yolu |
| `HowTo` | Adım adım rehber | Adım listesi görünümü |

Bir sayfada tipik olarak **üç ila dört** tür bulunur: `Organization` (referansla), sayfanın ana türü, `BreadcrumbList` ve varsa `FAQPage`. Bundan fazlası genelde birikmiş artıktır.

Bir uyarı: Google'ın zengin sonuç desteği zaman içinde değişiyor. `FAQPage` ve `HowTo` görünümleri geçmişte daraltıldı; bugün hangi türün nasıl gösterildiğini Google'ın kendi zengin sonuç galerisinden kontrol edin. Şemayı yazma sebebiniz yalnızca görsel zengin sonuç olmasın — makine okunabilirliğin kendisi zaten değerli.

## `Article` mı `BlogPosting` mi

Sık sorulan ve gereğinden fazla dert edilen bir soru.

`BlogPosting`, `Article`'ın alt türüdür. Yani `BlogPosting` yazdığınızda Google onu zaten `Article` olarak da anlar. Blog yazısı için `BlogPosting`, haber için `NewsArticle`, kurumsal bir doküman ya da rehber için `Article` mantıklı seçimlerdir — ama aralarındaki fark, alanların doğru doldurulmasının yanında önemsiz kalır.

Gerçekten fark yaratan alanlar şunlar:

```json
{
  "@type": "BlogPosting",
  "headline": "Sayfadaki H1 ile aynı olmalı",
  "datePublished": "2026-08-17T10:00:00+03:00",
  "dateModified": "2026-08-30T14:20:00+03:00",
  "author": { "@type": "Person", "name": "Ad Soyad", "url": "https://siteniz.com/hakkimizda" },
  "publisher": { "@id": "https://siteniz.com/#kurum" },
  "inLanguage": "tr",
  "mainEntityOfPage": { "@type": "WebPage", "@id": "https://siteniz.com/blog/yazi" }
}
```

Dört noktaya dikkat:

**`headline` sayfadaki başlıkla aynı olsun.** Farklı yazmak, "şemadaki bilgi sayfada görünmeli" kuralının ihlalidir.

**`dateModified` gerçek olsun.** Her dağıtımda "şimdi" yazan bir `dateModified`, [sitemap'te her URL'ye aynı `lastmod`'u yazmakla](/tr/blog/sitemap-lastmod-nedir-nasil-kullanilir) aynı güven sorununu yaratır.

**`author` bir varlığa bağlansın.** Sadece isim yazmak yetersiz; `url` ya da `@id` ile gerçek bir sayfaya bağlayın. Yapay zeka motorlarının "bunu kim söylüyor" sorusuna verdiği cevap burası.

**`publisher` `@id` ile referans verilsin**, kurum bilgisi tekrar yazılmasın.

Bizim blog sayfalarımızda bu graph şöyle kuruluyor: `Article` → `author` (Person) → `worksFor` (Organization), yanında `publisher`, `image`, `breadcrumb` ve varsa `FAQPage`. Hepsi tek `@graph` içinde, `@id` referanslarıyla bağlı. Ayrı ayrı duran düz varlıklar bağlı bilgi üretmiyor.

## İkinci tuzak: sayfada olmayan şey şemada olmamalı

Denetimde karşılaştığımız ikinci sorun buydu.

Yapısal veri, sayfada **görünen** içeriği makine okunur hale getirmek içindir. Sayfada bulunmayan bir bilgiyi şemaya yazmak — fiyat, puan, yorum sayısı — Google'ın yapısal veri politikalarına aykırıdır ve manuel işlem sebebidir.

Bizim ürün sayfalarımızda fiyat teklif usulü belirleniyor, sayfada fiyat yazmıyor. Bu yüzden şemada `offers` alanını **bilinçli olarak boş bıraktık**. Uydurma bir fiyat yazmak zengin sonuç getirebilirdi ama riski taşımaya değmez.

Aynı kural yorumlar için de geçerli: sayfada görünmeyen `AggregateRating` yazmayın. Gerçek olmayan yorum yayınlamak zaten ayrı bir sorun.

Bu maddede en sık düşülen hata `AggregateRating`'i "kendi hakkında" yazmak: kendi sitenizde kendi hizmetinize beş yıldız vermek. Google'ın kuralı açık — bir varlık kendi kendini derecelendiremez.

## Üçüncü tuzak: alan adı uyuşmazlığı

Bu, bizim kendi kodumuzda bulduğumuz ve en sevdiğimiz hata.

SSS verileri veritabanında `{q, a}` alanlarıyla saklanıyordu. Şema üreten kod ise `{question, answer}` okuyordu. Kod hata vermiyordu — JavaScript'te olmayan bir alanı okumak `undefined` döner, çökme olmaz.

Sonuç: sekiz yazıda `FAQPage` şeması şöyle yayınlanıyordu:

```json
{
  "@type": "Question",
  "name": undefined,
  "acceptedAnswer": { "@type": "Answer", "text": undefined }
}
```

Geçersiz JSON, geçersiz şema, hiçbir uyarı yok. Sayfa açılıyor, her şey normal görünüyor.

Bu tür hataları yakalamanın en hızlı yolu ham çıktıda `undefined` aramaktır:

```bash
curl -s https://siteniz.com/blog/yazi | grep -c "undefined"
```

Sıfırdan büyük her sonuç incelenmeli. Aynı kontrolü `"null"` ve `"[object Object]"` için de yapın — üçü birlikte, şablon üreten kodun en sık verdiği üç sessiz hatayı kapsar.

## Dördüncü tuzak: çok dilli sitede kopyalanan şema

Türkçe ve İngilizce sürümü olan bir sayfada şemayı da çevirmeniz gerekir — ve genelde yarısı çevrilir.

Tipik tablo: `headline` ve `description` çevrilmiş, ama `inLanguage` her iki sürümde de `"tr"` kalmış. Ya da `BreadcrumbList` içindeki basamak adları Türkçe, URL'ler İngilizce sürümü gösteriyor. İkisi de Google'a "bu sayfa hangi dilde emin değilim" dedirtir.

Kontrol basit:

```bash
for l in tr en; do
  echo "── $l"
  curl -s https://siteniz.com/$l/blog/yazi | grep -o '"inLanguage":"[a-z-]*"'
done
```

İki satır farklı dönmeli. Aynı dönüyorsa şema tek dilden kopyalanmış demektir.

Aynı sayfada `hreflang` etiketlerinin de tutarlı olması gerekir. Var olmayan bir çeviriye `hreflang` vermek, hiç vermemekten kötüdür: Google karşılıklılık bulamazsa kümenin tamamını yok sayabilir. Bizim şemamızda çeviri bağı `translationKey` alanıyla kuruluyor ve `hreflang` yalnızca gerçekten eşleşen çift varsa yayınlanıyor — tekil yazılarda hiç yayınlanmıyor.

## Çakışmayı önleyen mimari

Tek seferlik temizlik yeterli değil; altı ay sonra biri yine sayfaya şema ekler. Kalıcı çözüm yapısal:

**1. Şema üretimi tek bir modülde toplansın.** `src/lib/schema.ts` gibi tek bir dosya, sayfa tipine göre `@graph` üretsin. Sayfalar JSON yazmasın, fonksiyon çağırsın.

**2. Sayfa bileşenleri ham `<script>` yazmasın.** Kod incelemesinde aranacak tek şey: `application/ld+json` metni sayfa dosyalarında geçiyor mu. Geçiyorsa yanlış katmandadır.

**3. Yayın öncesi otomatik kontrol.** Basit bir betik, derlenmiş sayfalarda `@type` tekrarını sayıp ikiden fazlaysa uyarsın. Bu bir sayfada beş dakikalık iş, yüz sayfada elle imkânsız.

**4. Şema değişince Rich Results Test'ten geçirin.** Alışkanlık haline gelmesi gereken tek manuel adım.

## Doğrulama: düzeldiğini nasıl anlarsınız

Düzeltmeyi yaptınız, peki işe yaradı mı?

**Hemen:** Rich Results Test'te sayfa tek bir ana varlık gösteriyor, hata ve uyarı yok.

**Birkaç gün içinde:** Search Console'da URL denetimi yapıp "Test edilen sayfa" görünümünde şemayı kontrol edin — Google'ın gördüğü hali budur, sizin tarayıcınızın gördüğü değil.

**Haftalar içinde:** Search Console → Geliştirmeler bölümünde ilgili öğe türünün "geçerli" sayısı yükselir. Bu rakamın hareket etmesi zaman alır; tarama sıklığınıza bağlıdır. Sayfalarınızın ne kadar çabuk yeniden tarandığını [sitemap lastmod yazısında](/tr/blog/sitemap-lastmod-nedir-nasil-kullanilir) anlattığımız tarama bütçesi sinyali belirliyor.

**Aylar içinde:** Zengin sonuçların arama sonuçlarında görünmesi. Burada bir uyarı: geçerli şema zengin sonuç **garantisi değildir**. Google şemayı okur, doğru bulur ve yine de göstermemeyi seçebilir. Şema koşuldur, garanti değil.

## Kontrol listesi

Bir sayfayı yayına almadan önce:

- [ ] Her `@type` sayfada **tek** kez geçiyor mu
- [ ] Şema tek bir yerden mi üretiliyor (layout **ya da** sayfa, ikisi birden değil)
- [ ] Şemadaki her bilgi sayfada **görünüyor** mu
- [ ] `name`, `description` gibi alanlar sayfadaki metinle tutarlı mı
- [ ] Ham çıktıda `undefined`, `null` ya da `[object Object]` geçmiyor
- [ ] `@id` değerleri mutlak URL ve sayfalar arasında sabit
- [ ] Telefon, adres, e-posta gibi bilgiler sitenin geri kalanıyla aynı mı

Son madde önemsiz görünür ama değil. Bizim sitemizde telefon numarası üç ayrı yerde yer tutucu olarak duruyordu ve **üçü de birbirinden farklıydı**: şemada bir numara, `tel:` bağlantısında başka, görünen metinde bir başkası. Google'ın kurumsal varlık eşleştirmesi bu tutarsızlıkları yakalar — bu konuyu [NAP tutarlılığı yazısında](/tr/blog/nap-tutarliligi-nedir) ayrı ele aldık.

## Özet

Yapısal veri "ne kadar çok o kadar iyi" değildir. Bir sayfa bir şey anlatmalı ve o şeyi bir kez anlatmalıdır.

Şemayı ortak bir bileşene taşıdığınızda eski satırı silmeyi unutmayın — çünkü unuttuğunuzda hiçbir hata almazsınız, sadece zengin sonuçlarınız sessizce kaybolur.

Ürün ve fiyat şemasının doğru kurulması özellikle e-ticarette doğrudan tıklama farkı yaratıyor; o tarafın kurulum sırasını [e-ticaret sitesi nasıl kurulur yazısında](/tr/blog/e-ticaret-sitesi-nasil-kurulur) topladık. Şema katmanının yapay zeka görünürlüğüyle ilişkisi için [GEO rehberindeki](/tr/blog/yapay-zeka-aramalarinda-gorunurluk-geo-rehberi-2026) schema graph bölümüne bakın. Kendi sayfalarınızda bu denetimi bizim yapmamızı isterseniz [hizmetler sayfasında](/tr/hizmetler) kapsam yazılı.

<!-- GORSEL: cakisan-yapisal-veri-inline-2 — Tek @graph yapısının şeması: ortada Organization, ondan oklarla Article, Product ve BreadcrumbList'e giden @id referansları -->
