feat(seo): GEO paketi — yapılandırılmış metadata, AI bot erişimi, veri tazelik sinyali

- sayfaMetadata(): indekslenebilir sayfalar için tek kaynaklı canonical +
  eksiksiz openGraph üretimi; sayfalar buna geçirildi
- robots.ts: yalnızca /api/ engellenir; noindex'li sayfalar robots engeli
  yerine meta ile yönetilir, AI botları niyeti belgeleyen açık grupla izinli
- next.config: htmlLimitedBots'a AI botları eklendi (JS çalıştırmayan
  botlara metadata head içinde bloklanarak verilir)
- sitemap lastmod + katalogda görünür 'Kaynak/son güncelleme' satırı
  (VeriKaynagi) KATALOG_VERI_TARIHI tek kaynağından
- docs/geo/geo-raporu.md: çalışmanın raporu

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
bilalgursen
2026-09-21 00:51:16 +03:00
parent d2651eadb9
commit 53eb07bb34
18 changed files with 718 additions and 73 deletions

462
docs/geo/geo-raporu.md Normal file
View File

@@ -0,0 +1,462 @@
# KolayTercih — GEO (Generative Engine Optimization) Raporu ve Yapılacaklar
**Tarih:** 21 Eylül 2026 · **Kapsam:** ChatGPT, Perplexity, Google AI Bakışı / AI Modu, Gemini, Copilot ve Claude'da KolayTercih'in kaynak gösterilmesi ve önerilmesi.
**Yöntem.** Dört paralel web araştırması (akademik kanıt, teknik GEO, platform/ölçüm, YKS nişi ve rakipler) ve bir kod tabanı denetimi yapıldı. Kod bulgularının kritik olanları canlı sitede `curl -A GPTBot` ile ayrıca doğrulandı.
**Kanıt işaretleri.** Rapor boyunca:
- **[D]** — birincil kaynak açılıp okundu ya da kodda/canlı sitede doğrulandı.
- **[A]** — yalnızca ikincil kaynak ya da arama özeti; temkinle kullanın.
- **[Ç]** — kanıttan yapılmış çıkarım; ölçülmüş değil.
**Veri kısıtı.** Ahrefs, Similarweb ve Rybbit bağlantıları bu oturumda kullanılamadı. Rapor gerçek trafik verisine değil, açık web kaynaklarına ve koda dayanıyor. LLM'lere Türkçe tercih sorguları sorup kaynaklarını kaydetme testi de yapılamadı; bu, ölçüm planının ilk işi (bkz. §8).
---
## 0. Yönetici özeti
1. **Zemin sağlam.** 1.579 sayfa statik prerender ediliyor, ana içerik sunucu tarafında HTML olarak geliyor, `Reveal` efekti `@media (scripting: enabled)` ile korunuyor, hiçbir katman AI botlarını engellemiyor. GPTBot, ClaudeBot ve PerplexityBot user-agent'larıyla site 200 dönüyor. [D]
2. **En büyük yapısal avantaj:** YÖK Atlas JavaScript olmadan boş bir kabuk döndürüyor (`<div id="root"></div>`). JS çalıştırmayan AI botları resmî veriyi oradan okuyamaz. Bu veriyi düz HTML tablo olarak sunan üçüncü taraf siteler fiilen tek okunabilir kaynak. [D] KolayTercih bunu zaten yapıyor; eksik olan derinlik, tazelik sinyali ve güven katmanı.
3. **Canlıda duran üç hata doğrudan kayıp yaratıyor:**
- 870'ten fazla alt sayfada `og:title` ve `og:url` ana sayfanınkini gösteriyor. [D]
- Ana sayfadaki SSS cevapları görünür HTML'de yok; yalnızca JSON-LD içinde. [D]
- `www.kolaytercih.com` DNS'te çözülmüyor. [D]
4. **Veri bir yıl geride.** Veritabanında 2021–2025 var, 2026 yerleştirme verisi yok (18 Ağustos 2026'da açıklandı). [D] Rakipler başlıklarını şimdiden "2027"ye çevirdi. Tazelik, özellikle ChatGPT ve Perplexity'de alıntıyla ilişkili.
5. **En değerli özgün veri görünmez.** 62.975 kayıtlık `netler` tablosu yalnızca tıklayınca açılan bir modalda, istemci tarafı fetch ile geliyor. Hiçbir bot göremiyor. [D]
6. **Rakipler GEO'ya başlamış.** kocasor.com sıralama bandı sayfaları, karşılaştırma sayfaları, `ItemList` + `FAQPage`, llms.txt ve açık AI bot izinleriyle nişin en olgun sitesi. unitercihrobotu.com 24 tercihi Riskli/İdeal/Garanti diye bölüyor — konumlanması KolayTercih'e en yakın olan bu. [D]
7. **Site dışı sinyaller sayfa içi rötuşlardan güçlü.** AI görünürlüğüyle en yüksek korelasyon YouTube anılmaları (0,71–0,74) ve web'deki marka anılmaları (0,66–0,71); Domain Rating yalnızca 0,27–0,33. [D] Nedensellik kanıtlanmış değil, ama yön açık: yeni bir marka için asıl iş başkalarının KolayTercih'ten söz etmesi.
8. **Beklenti.** AI yönlendirme trafiği tipik olarak toplam trafiğin ~%1'i. [D] Asıl etki dolaylı: AI Bakışı organik tıklamayı %38–58 düşürüyor, AI Bakışı'nda alıntılanmak bu kaybı kısmen telafi ediyor. [D] ChatGPT yanıtlarının çoğu web'e çıkmadan, modelin hafızasından geliyor; yeni bir markanın oraya girmesi 6–18 ay sürer. [Ç]
9. **Takvim lehimize.** Bir sonraki tercih dönemi Temmuz sonu 2027. İçeriğin taranıp arama dizinlerine oturması haftalar–aylar sürdüğü için işin Eylül–Aralık 2026'da yapılması gerekiyor. 2026, Türkiye'de AI Bakışı ile geçen ilk tercih sezonuydu (Google 18 Şubat 2026'da açtı). [D]
**En yüksek getirili beş hamle:** (1) OG miras hatasını düzeltmek, (2) SSS cevaplarını HTML'e almak, (3) 2026 verisini yükleyip katalog sayfalarına güncelleme tarihi ve `lastmod` eklemek, (4) sıralama bandı sayfaları + bölüm sayfalarını derinleştirmek (net verisi dahil), (5) yıllık "taban sıralama değişim raporu" yayınlayıp basına ve YouTube kanallarına servis etmek.
---
## 1. GEO nedir, kanıt ne diyor
### 1.1 Tanım
GEO, içeriğin yapay zekâ cevaplarının içinde **kaynak gösterilmesi** (alıntı) ve markanın **önerilmesi** (anılma) için yapılan çalışmadır. Terim Aggarwal ve arkadaşlarının KDD 2024 makalesinden geliyor. AEO ve LLMO aynı işin başka adları; taktikler ~%90 örtüşüyor. [A]
Klasik SEO'dan farkı: tek bir sıralama yok. Süreç aşamalı ve olasılıksal — **tetiklenme → getirme (retrieval) → yeniden sıralama → üretim → alıntı**. Aynı soru iki kez sorulduğunda farklı kaynaklar gelebiliyor.
Google'ın resmî pozisyonu (Mayıs 2026): "Üretken yapay zekâ araması için optimizasyon hâlâ SEO'dur." AI Bakışı ve AI Modu için ek bir şart yok; sayfanın indekslenmiş ve snippet'e uygun olması yeterli. [D] Bu yalnızca Google için geçerli; ChatGPT ve Perplexity'nin kaynak seçimi farklı.
### 1.2 Akademik kanıt
**Orijinal GEO makalesi** (10.000 sorgu, 9 taktik) [D]:
| Taktik | Görünürlük etkisi |
|---|---|
| Alıntı (uzman sözü) ekleme | ~+%41 |
| İstatistik ekleme | ~+%33 |
| Akıcılık iyileştirme | ~+%29 |
| Kaynak gösterme | ~+%28 |
| Anahtar kelime doldurma | **taban çizgisinin altında** |
Düşük sıradaki siteler daha çok kazandı: kaynak gösterme ile 5. sıradaki site +%115, 1. sıradaki −%30.
**Sonraki çalışmalar bu tabloyu yumuşatıyor** [D]:
- %40, tek metrikte ve kaynağın zaten modele verilmiş 5 belge içinde olduğu bir düzenekte elde edilmiş tavan değer. Keşfedilebilirliği, tıklamayı ve dönüşümü ölçmüyor.
- C-SEO Bench (NeurIPS 2025): yöntemlerin çoğu etkisiz, sık sık negatif. Herkes aynı taktiği uygulayınca kazanç sıfır toplamlı oluyor. Bağlamda üst sıraya çıkmak çoğu yeniden yazımdan daha etkili.
- Chen ve ark. 2025: AI motorlarında üçüncü taraf, otoriter kaynaklar lehine sistematik yanlılık var; markanın kendi sitesi geri planda kalıyor.
### 1.3 Kanıt gücüne göre özet
| Kanıt | Ne işe yarıyor |
|---|---|
| **Güçlü** | Gerçek bilgi ihtiyacına doğrudan alaka; getirme aşamasına girebilmek (indekslenebilirlik, klasik SEO, alt soruları kapsayan konu derinliği) |
| **Orta** | Doğrulanabilir ve tarihli istatistik, tanım, karşılaştırma; cevabı başa almak; zamana duyarlı konularda tazelik; orijinal veri |
| **Korelasyon (nedensel değil)** | Üçüncü taraf anılmaları, YouTube anılmaları, kazanılmış medya |
| **Zayıf / yok** | llms.txt, tek başına schema, anahtar kelime doldurma, yapay parçalama, otoriter ton |
### 1.4 Sektör bulguları
- **Sayfa içi konum.** İncelenen 18.012 ChatGPT alıntısının %44'ü metnin ilk %30'undan geliyor. Alıntılanan cümleler tanım içeren, varlık (entity) yoğun, kesin dilli cümleler. [A] → Her sayfanın ilk paragrafı kendi başına anlamlı bir olgusal özet olmalı.
- **Sorgu yayılımı (query fan-out).** Google ve ChatGPT soruyu alt sorgulara bölüyor. Alt sorgularda da sıralanan sayfanın alıntılanma olasılığı %161 daha yüksek. [D] AI Bakışı alıntılarının organik ilk 10'dan gelme oranı %76'dan %38'e düştü. [D] → Tek anahtar kelime sayfası değil, konu kümesi.
- **Tazelik.** AI'nın alıntıladığı sayfalar organik sonuçlardan %25,7 daha taze; en çok ChatGPT tazeliği ödüllendiriyor. İçerik değişmeden tarih değiştirmek önerilmiyor. [D]
- **Liste yazıları.** ChatGPT'nin alıntıladığı sayfa tiplerinin %43,8'i "en iyi X" listeleri; %35'i DR<30 sitelerden. [D] Ama markanın kendini 1. sıraya koyduğu liste alıntılandığında vakaların %69'unda rakip öneriliyor. [A] → Asıl kaldıraç başkalarının listelerinde yer almak.
- **Schema.** Ahrefs'in kontrollü karşılaştırmasında JSON-LD eklemenin alıntıya etkisi pratikte sıfır. [D] Canlı fetch'te hiçbir büyük model yalnızca JSON-LD'de bulunan veriyi okumadı. [D] Schema bir alıntı kaldıracı değil, varlık netliği altyapısı. **Kural: JSON-LD'ye konan her olgu görünür HTML'de de bulunmalı.**
- **llms.txt.** ~300.000 alan adında alıntıyla ilişki yok. [A] Google kullanmadığını söylüyor. [D] Zararsız, ucuz, kanıtlanmış faydası yok.
**Uyarı:** Bulguların neredeyse tamamı ABD ve İngilizce verisine dayanıyor. Türkçe sorgulara genellemek belirsiz.
---
## 2. Platformlar kaynağı nasıl seçiyor
| Platform | Altyapı | Pratik sonuç |
|---|---|---|
| **ChatGPT** | Kendi indeksi (OAI-SearchBot) + Bing + başka sağlayıcılar. Yanıtların çoğunda web'e hiç çıkmıyor; ABD'de yanıtların yalnızca ~%7'si alıntı içeriyor. [D] | OAI-SearchBot'a izin + Bing'de indeksli olmak. Aramasız yanıtlar için marka anılması belirleyici. |
| **Perplexity** | Kendi tarayıcısı ve indeksi; her yanıtta alıntı. Türkçe sorgularda yanıt başına ~16 kaynak. [D] | Yeni sayfalar günler içinde alıntılanabiliyor. En hızlı geri bildirim veren platform. |
| **Google AI Bakışı / AI Modu** | Google indeksi + sorgu yayılımı. | Klasik SEO + alt soruları kapsayan sayfa kümesi. En çok alıntılanan alan adı YouTube. [D] |
| **Copilot** | Bing. | Bing Webmaster Tools + IndexNow doğrudan kaldıraç. |
| **Claude** | Alıntıları Brave Search sonuçlarıyla yüksek örtüşme gösteriyor. [A] | Brave'de indeks kontrolü. |
| **Gemini** | Google indeksi; canlı fetch'te JS render edebilen tek sistem. [D] | Google'da iyi olan burada da iyi. |
**Oynaklık.** ChatGPT'de Reddit'in payı Ağustos 2025'te yanıtların ~%60'ından Eylül ortasında ~%10'a çöktü; Ağustos 2026'da benzer bir düşüş yine yaşandı. Modeller 7 ayda sosyal kaynak tercihini 16 kez değiştirdi, hiçbiri duyurulmadı. [D] → Tek platforma bağımlı strateji kırılgan.
**Türkiye'ye özgü bulgular (kanıt zayıf)** [D, küçük örneklemler]:
- Türkçe keşif sorgularında yanıtların %58'i dizin ve liste sitelerine dayanıyor; en çok alıntılananlar arasında YouTube var.
- ChatGPT Türkçe ticari sorgularda İngilizce yabancı kaynaklara atlıyor; Perplexity Türkçede kalıyor ama zayıf kaynaklar alıntılıyor. Bağımsız Türkçe inceleme kaynağı boşluğu var.
- Ekşi Sözlük, DonanımHaber, Technopat veya r/YKS'nin Türkçe LLM yanıtlarındaki payını ölçen metodolojisi açık bir çalışma **bulunamadı**. Ajans yazılarındaki "Ekşi, Türkçenin Reddit'idir" türü ifadeler veriye değil görüşe dayanıyor.
→ Türkiye için en sağlıklı yol kendi verimizi üretmek (§8).
---
## 3. Trafik gerçekliği ve beklenti yönetimi
- AI yönlendirme trafiği toplam trafiğin ~%1'i (13.770 alan adı, 3,3 milyar oturum); %87'si ChatGPT'den. [D]
- Dönüşüm üstünlüğü kanıtlanmış değil: 54 sitelik çalışmada LLM trafiği %4,87, organik %4,60 — fark anlamsız. [D]
- AI Bakışı varken geleneksel sonuçlara tıklama %15'ten %8'e düşüyor. [D] 1. pozisyonun tıklama oranı iki yılda %58 düştü. [D]
- AI Bakışı'nda alıntılanan marka, alıntılanmayana göre %120 daha fazla tıklama alıyor; ama yine de AI Bakışı'nın olmadığı duruma göre %38 geride. [D]
- ABD'de Google aramalarının %68'i tıklamasız bitiyor. [D]
- Mayıs 2026'daki ChatGPT arayüz güncellemesinden sonra ChatGPT kaynaklı ziyaretlerde ana sayfaya gelenlerin payı %26–29'dan %62–63'e çıktı. [D] → Ana sayfanın dönüşüm kalitesi daha kritik.
- ChatGPT kaynaklı ziyaretlerin çoğu analitikte "direkt" ya da markalı organik olarak görünüyor. [A] → **Markalı arama hacmi** (Search Console) en iyi dolaylı gösterge.
**Yansıma süreleri** [Ç]:
| Süre | Ne yansır |
|---|---|
| Günler–2 hafta | Perplexity, Copilot, ChatGPT search'te yeni/güncel sayfaların alıntılanması (IndexNow hızlandırır) |
| 2–8 hafta | Google AI Bakışı / AI Modu |
| 1–3 ay | Üçüncü taraf listelere ve haberlere girmenin anılma oranına yansıması |
| 6–18 ay | Modelin hafızasından (aramasız) markanın bilinmesi |
---
## 4. Niş: YKS tercih ekosistemi
### 4.1 Rakipler
| Site | Ne yapıyor | GEO sinyali |
|---|---|---|
| **kocasor.com** | 722 URL: `/siralama/{aralık}-{puan türü}` (49), `/karsilastirma/x-vs-y` (40), `/universite` (223), `/bos-kontenjan`, `/puan-araligi`, araçlar | `ItemList` + `FAQPage`, karşılaştırmalarda `dateModified` + 4 HTML tablo, llms.txt, AI botlarına açık izin. **Nişin en olgunu.** |
| **unitercihrobotu.com** | 1.389 URL, çoğu "X üniversitesi 2026 taban puanları"; 24 tercihi Riskli/İdeal/Garanti diye bölüyor | `SoftwareApplication` + `AggregateRating`, llms.txt, AI botlarına açık izin. Sayfaları ince (~18 KB, JSON-LD yok). |
| **basarisiralamalari.com** | WordPress, son 4 yıl tabloları; başlığı şimdiden "2027 Tercih Rehberi" | Yazar adı boş — zayıflık. |
| **hangiuniversite.com** | Üsküdar Üniversitesi yayını; üniversite, bölüm, ücret sayfaları | llms.txt; AI botlarına markdown sunduğunu söylüyor. |
| **eleman.net/tercih-robotu** | Sıralama aralığı sayfaları, tek sayfada 21 HTML tablo | JSON-LD yok. |
| tercih-ai.com, tercihsepeti, canlikoc, ayna360 | Tek sayfa araçlar | `FAQPage` + `WebApplication`. |
| Hürriyet, Milliyet, CNN Türk | Takvim ve tarih sorgularına hâkim; uzman imzalı köşe yazıları | Haber otoritesi. |
**Boş alanlar** [Ç]: düzenli yıllık veri raporu (hiçbir rakipte yok), üniversite–üniversite ve maliyet hesaplı vakıf–devlet karşılaştırmaları, "yapay zekâ ile tercih yapılır mı" sorgu kümesi, net ortalamaları verisi, gerçek yazar/uzman imzası, geriye dönük doğruluk testi.
### 4.2 Soru envanteri (niyete göre)
**Bilgi**
- Tercih listesi nasıl yapılır? Kaç tercih yazmalıyım? 24'ü doldurmak zorunlu mu?
- Tercihler puana göre mi sıralamaya göre mi yapılır? Tercih sırası önemli mi?
- Hayal / gerçekçi / garanti dağılımı nasıl olmalı? Tercih aralığı sıralamamın kaç bin altını-üstünü kapsamalı?
- Ölü tercih nedir? OBP nedir, kırık OBP nedir?
- Taban sıralamalar bu yıl düşer mi yükselir mi?
- Başarı sırası şartı hangi bölümlerde var? Özel koşullar, okul birinciliği, 34 yaş üstü kadın kontenjanı nasıl işliyor?
- Ek tercih nasıl yapılır, kimler yapabilir? Yerleşemedim, ne yapmalıyım?
- Tercih, sonuç ve kayıt tarihleri ne zaman?
**Karşılaştırma**
- Vakıf burslu mu devlet mi? %50 burslu okunur mu?
- Bölüm mü, üniversite mi, şehir mi?
- Bilgisayar mühendisliği mi yazılım mühendisliği mi? (ve benzerleri)
- Önlisans mı lisans mı? İngilizce mi Türkçe mi?
**Araç arama**
- En iyi / güvenilir / ücretsiz tercih robotu hangisi?
- Yapay zekâ ya da ChatGPT ile tercih yapılır mı?
- Tercih danışmanlığına para verilir mi? (Ekşi'de baskın görüş kuşkucu → fiyat ve yöntem şeffaflığı güven içeriği.)
- **"X sıralamayla hangi bölümler gelir?"** — puan türü × 5–50 binlik dilimler hâlinde çok aranıyor.
- Netlerimle sıralamam kaç olur?
**İşlem**
- AİS'te tercih nasıl girilir ve onaylanır? Tercih değiştirilebilir mi?
Mevcut 24 rehber yazısı bilgi niyetinin büyük kısmını kapsıyor. Açık kalanlar: sıralama bandı sayfaları, sözlük, karşılaştırmalar, "AI ile tercih" sayfası, net/sıralama hesaplama.
### 4.3 Takvim
**2026 (gerçekleşen, resmî)** [D]: sınav 20–21 Haziran · sonuç 21 Temmuz · tercih 29 Temmuz–10 Ağustos · yerleştirme 18 Ağustos · kayıt 24–28 Ağustos · ek tercih 17–21 Eylül (bugün son gün).
**2027:** ÖSYM takvimi henüz yayınlamadı. 2026 örüntüsüyle kaba beklenti [Ç]: sınav Haziran üçüncü haftası, sonuç Temmuz üçüncü haftası, tercih Temmuz sonu–Ağustos ilk 10 günü.
### 4.4 Veri atfı ve hukuki not
YÖK Atlas için yayımlanmış bir kullanım koşulu ya da lisans metni **bulunamadı**; ÖSYM'nin telif sayfası 404 verdi. ÖSYM'nin bilinen sıkı telif tutumu sınav sorularıyla ilgili; taban puan istatistikleri için açık bir hüküm bulunamadı. **Ticari kullanım için hukuki teyit alınması önerilir.**
Önerilen atıf biçimi (her tablonun altında): *"Kaynak: ÖSYM 2026-YKS Yerleştirme Sonuçları, YÖK Atlas · veri yılı 2026 · son güncelleme: <tarih>"* + resmî sayfaya bağlantı. Hesaplanmış değerler (risk, trend) resmî veriden görsel ve metinsel olarak ayrılmalı.
---
## 5. Mevcut durum denetimi
### 5.1 İyi yapılmış olanlar [D]
- 1.579 rota statik prerender: 939 bölüm (635 + 304 sayfalama), 592 üniversite (229 + 363), 24 rehber.
- Katalog tabloları istemci bileşeni olsa da veri prop olarak geliyor; **50 satır ilk HTML'de var** (canlıda doğrulandı: `/bolum/hukuk` → 51 `<tr>`).
- `Reveal` gizlemesi `@media (scripting: enabled)` içinde + 3 saniyelik emniyet animasyonu ([globals.css:196](../../src/app/globals.css)). JS'siz bot içeriği görüyor. `ScrollFlow` ve `Parallax` yalnızca `transform` uyguluyor.
- Canonical tüm indekslenebilir sayfalarda var ve `<head>` içinde (canlıda doğrulandı).
- JSON-LD tek kaynaktan ([seo.tsx](../../src/lib/seo.tsx)): Organization, WebSite, SoftwareApplication, FAQPage, BreadcrumbList, Article; üniversite sayfalarında `CollegeOrUniversity`. `<` kaçışı yapılıyor.
- `/sonuc` için robots.txt–noindex tuzağı doğru çözülmüş ve yorumla belgelenmiş.
- YÖK Atlas atfı ve "yerleşme garantisi yok" beyanı 6 ayrı yerde.
- Middleware, UA filtresi, WAF, Cloudflare proxy yok → AI botlarını engelleyen katman yok.
- Rybbit analitiği kurulu, sunucu tarafı dönüşüm takibi var, PII maskeleniyor.
### 5.2 Hatalar ve eksikler
| # | Bulgu | Kanıt | Önem |
|---|---|---|---|
| E1 | **OG miras hatası.** Kök layout'ta `openGraph.url: "/"`, `title`, `description` tanımlı; alt sayfalar `openGraph` vermediği için hepsi bunu miras alıyor. Canlıda `/bolum/hukuk` → `og:title="KolayTercih — Yapay Zekâ Destekli…"`, `og:url="https://kolaytercih.com"`. 870+ sayfa. OG görselleri ise sayfaya özel — başlıkla tutarsız. | [layout.tsx:57-65](../../src/app/layout.tsx) · canlıda doğrulandı | P0 |
| E2 | **SSS cevapları HTML'de yok.** Radix akordeon kapalıyken içeriği render etmiyor. Canlıda cevap metni yalnızca JSON-LD içinde bulundu. Sitenin en "alıntılanabilir" metinleri (veri kaynağı, garanti vermeme, rakip farkı) botlara görünmüyor; ayrıca JSON-LD ≠ görünür içerik. | [page.tsx:595-615](../../src/app/page.tsx) · [accordion.tsx:58-68](../../src/components/ui/accordion.tsx) · canlıda doğrulandı | P0 |
| E3 | **`www.kolaytercih.com` DNS'te çözülmüyor.** Traefik kuralı da yalnızca apex'i eşliyor. www ile verilen her bağlantı ölü. | [docker-compose.yml:22](../../docker-compose.yml) · `curl` doğrulandı | P0 |
| E4 | **2026 yerleştirme verisi yok.** `programs` tablosunda 2021–2025, `netler`'de 2023–2025. Veritabanı 9 Ağustos tarihli. Sayfa başlıkları "2026" diyor. | `sqlite3` doğrulandı | P0 |
| E5 | **Katalog sayfalarında tazelik sinyali yok.** Sitemap'te bölüm/üniversite girdilerinde `lastModified` yok (864 sayfa); sayfa gövdesinde "veri şu tarihte güncellendi" yok. Bing `lastmod`'u "kilit sinyal" sayıyor, `priority`/`changefreq`'i yok sayıyor. | [sitemap.ts:21-32](../../src/app/sitemap.ts) | P0 |
| E6 | **Net verisi botlara görünmez.** 62.975 kayıt yalnızca modal + `/api/netler` istemci fetch'i ile. | `program-netleri.tsx` | P1 |
| E7 | **Bölüm sayfaları ince.** h1 + ~15 kelime + tablo; 635 sayfa aynı şablon. 2021–2024 verisi yalnızca sparkline olarak, sayısal HTML yok. Google'ın "ölçekli içerik" politikası açısından da risk. Üniversite sayfaları belirgin biçimde daha iyi. | [bolum-icerik.tsx:109-118](../../src/features/katalog/components/bolum-icerik.tsx) | P1 |
| E8 | **E-E-A-T boşlukları.** `/hakkinda` yok; 24 rehberin hiçbirinde yazar yok; `Article`'da `author` ve `dateModified` yok; rehber tarihi sayfa gövdesinde görünmüyor. | [seo.tsx:92-112](../../src/lib/seo.tsx) · [rehber.ts:20-26](../../src/lib/rehber.ts) | P1 |
| E9 | **"Tercih robotu" landing'i yok.** `/tercih-robotu` 301 ile `/#hero-form`'a gidiyor; Google fragment'i yok sayar. Nişin en yüksek hacimli araç arama sorgusu için sayfa yok. | [next.config.ts:63](../../next.config.ts) | P1 |
| E10 | **robots.txt + noindex çelişkisi.** `/listem`, `/odeme/`, `/rapor/`, `/giris` hem disallow hem noindex; bot sayfayı indiremediği için noindex'i göremez. | [robots.ts:11](../../src/app/robots.ts) | P2 |
| E11 | **Beş rehber URL'sinde yıl var:** `ek-yerlestirme-2026`, `siralama-sartlari-2026`, `tercih-sonuclari-ne-zaman-2026`, `universite-kayit-rehberi-2026`, `yks-tercih-takvimi-2026`. Her yıl yeni URL = birikmiş sinyalin sıfırlanması. | `content/rehber/` | P2 |
| E12 | **Header'da içerik navigasyonu yok;** `/bolumler`, `/universiteler`, `/rehber` yalnızca footer'da. Footer kolon başlıkları `<h2>` — her sayfada 3 fazladan h2. | [site-header.tsx](../../src/components/site-header.tsx) · [site-footer.tsx:21,39,87](../../src/components/site-footer.tsx) | P2 |
| E13 | **h1 yalnızca slogan** ("Dört yıllık geleceğini 48 saatlik panikle seçme"); "YKS tercih" ya da marka geçmiyor. | `hero-baslik.tsx` | P2 |
| E14 | **SSS'de "son 4 yıl"** yazıyor; veritabanında 5 yıl var, tabloda "Son 5 yıl" yazıyor. LLM'ler tutarsız olguları cezalandırır. | [page.tsx:121](../../src/app/page.tsx) | P2 |
| E15 | Schema eksikleri: katalogda `ItemList`, bölümde `EducationalOccupationalProgram`, `Organization.sameAs`, `WebSite.potentialAction`, `/paket`'te `Offer`. (Etkisi sınırlı — §1.4.) | [seo.tsx](../../src/lib/seo.tsx) | P2 |
| E16 | `verification` metadata yok; Search Console / Bing doğrulamasının repoda izi yok. 667 sayfalama URL'i sitemap dışında. llms.txt yok. AI trafiğini ayıran analitik segmenti yok. Traefik erişim logu üzerinden bot takibi yok. | — | P1–P3 |
**Next.js 16 inceliği.** `generateMetadata` istek anına ertelenirse, "HTML-limited bot" listesinde olmayan user-agent'lar için meta etiketleri `<body>`'ye akıtılır. GPTBot, ClaudeBot, PerplexityBot varsayılan listede **yok**. Şu an tüm herkese açık sayfalar prerender edildiği için sorun yok (canlıda doğrulandı), ama ileride dinamik bir herkese açık sayfa eklenirse sessizce bozulur. Sigorta: `next.config.ts`'te `htmlLimitedBots` regex'ini genişletmek.
### 5.3 GEO kapsamı dışında ama denetimde görülenler
- Gizlilik ve Koşullar sayfalarında kodda "MVP taslağıdır; yayına almadan önce hukuki gözden geçirme gerekir" notu duruyor; sayfalar canlı ve ödeme alınıyor.
- El yazısı fontu için kodda "canlıya çıkmadan ticari lisans alınmalı" notu duruyor; font canlıda.
- Rate limit yok; `/api/soru` OpenRouter maliyeti üretiyor.
- HSTS / CSP gibi güvenlik başlıkları yok.
---
## 6. Strateji
Dört sütun, öncelik sırasıyla:
**1 — Zemin: okunabilirlik ve tazelik.** Botların gördüğü HTML'i düzelt (E1, E2), veriyi güncelle (E4), tazelik sinyallerini ekle (E5), Bing/IndexNow/Brave kaydını yap. Ucuz, hızlı, kesin.
**2 — İçerik: YÖK Atlas'ın okunabilir yüzü olmak.** Resmî veriyi düz HTML + doğal dilde özet cümleleriyle sunan, sorgu yayılımındaki alt soruları kapsayan sayfa kümeleri. Her sayfa **deterministik olarak veriden türetilmiş** özgün değer taşımalı (trend, medyan, dağılım, karşılaştırma) — serbest LLM dolgu metni değil. Bu, ürünün "yapay zekâ havuz dışına çıkamaz" ilkesiyle de tutarlı.
**3 — Güven: YMYL'ye yakın bir kararda neden bize inanılsın.** Metodoloji sayısal eşikleriyle açık, veri kaynağı şeffaf, gerçek kişi imzalı, güncelleme tarihi görünür, doğruluk geriye dönük test edilmiş.
**4 — Site dışı: başkalarının bizden söz etmesi.** Yıllık veri raporu → basın ve YouTube; üçüncü taraf listeler; topluluklarda şeffaf katılım. En güçlü korelasyon burada, en yavaş yansıma da burada.
**Ölçüm** dört sütunun altında sürekli çalışır (§8).
---
## 7. Yapılacaklar
Efor: **S** ≤ yarım gün · **M** 1–3 gün · **L** 1 haftadan fazla.
### P0 — Bu hafta (canlıdaki hatalar ve hızlı kazanımlar)
- [x] **T1 · OG miras hatasını düzelt** (S) — *yapıldı 21.09.2026, build çıktısında doğrulandı; deploy bekliyor.* Kök layout'taki `openGraph`'tan `url`, `title`, `description`'ı çıkar; [seo.tsx](../../src/lib/seo.tsx)'e `title`/`description`/`path` alıp `alternates.canonical` + tam `openGraph` nesnesi (siteName, locale, type, url dahil) üreten tek bir yardımcı ekle; tüm sayfa `metadata`/`generateMetadata`'larını buna geçir. Rehberdeki mevcut override `siteName`/`locale`/`url` kaybediyor — o da aynı yardımcıya geçsin. `twitter:title/description` da buradan üretilsin. *Doğrulama:* `curl -s https://kolaytercih.com/bolum/hukuk | grep og:`.
- [x] **T2 · SSS cevaplarını HTML'e al** (S) — *yapıldı 21.09.2026 (`forceMount` + grid-rows geçişi), build çıktısında doğrulandı; deploy bekliyor. "Son 4 yıl" ifadesi pazarlama metninde 6 yerde tutarlı kullanıldığı için dokunulmadı — ürün kararı.* `AccordionContent`'e `forceMount` ver ve kapalı durumu CSS ile gizle; ya da SSS'yi akordeonsuz düz liste yap. Aynı anda "son 4 yıl" ifadesini gerçek veriyle eşitle (E14). *Doğrulama:* `curl -s -A GPTBot https://kolaytercih.com | grep "veri olmadan tahmin"`.
- [ ] **T3 · www'yi çöz** (S). DNS'e `www` kaydı ekle, Traefik'e `www` host kuralı + apex'e 301 yönlendirme middleware'i ekle.
- [ ] **T4 · Bing Webmaster Tools'a kaydol** (S). Search Console'dan import edilebiliyor; sitemap gönder. AI Performance raporu (Copilot alıntıları, grounding sorguları) buradan geliyor. Search Console doğrulamasını da teyit et; `verification` metadata'sını ekle.
- [ ] **T5 · Brave Search'te indeks kontrolü** (S). `site:kolaytercih.com` araması; yoksa Brave'in site gönderim formunu kullan.
- [ ] **T6 · Rybbit'te AI segmenti kur** (S). Referrer regex'i: `chatgpt\.com|chat\.openai\.com|perplexity\.ai|gemini\.google\.com|copilot\.microsoft\.com|claude\.ai|deepseek\.com|grok\.com|meta\.ai` + `utm_source=chatgpt.com`. Bugünkü durumu kaydet (taban çizgisi).
- [ ] **T7 · Prompt taban çizgisini al** (M). §8.2'deki prompt setini 4 platformda elle bir kez koş; alıntılanan alan adlarını say. Bu tablo site dışı hedef listesini ve içerik önceliklerini doğrudan verir.
### P1 — 2–6 hafta (Eylül–Ekim 2026: veri ve derinlik)
- [ ] **T8 · 2026 yerleştirme verisini yükle** (M–L). ÖSYM'nin "En Küçük ve En Büyük Puanlar" TABLO-3/TABLO-4 Excel dosyalarından `sira2026`, `puan2026`, `kontenjan2026`, `yerlesen2026`. Ardından başlıkları ve açıklamaları 2027 sezonuna çevir. **Meraklısına şemasını ve anlatımı kontrol et** (yıl sayısı değişiyorsa aynı commit'te güncelle).
- [x] **T9 · Tazelik sinyalleri** (S) — *(a) ve (b) yapıldı 21.09.2026: `KATALOG_VERI_TARIHI` sabiti ([seo.tsx](../../src/lib/seo.tsx)), sitemap `lastmod`, `VeriKaynagi` bileşeni. (c) JSON-LD `dateModified` T10 ile birlikte. **Veri her yenilendiğinde sabiti güncelle.*** Veri güncelleme tarihini tek bir sabitten üret; (a) [sitemap.ts](../../src/app/sitemap.ts)'te bölüm/üniversite girdilerine gerçek `lastModified`, (b) katalog sayfalarında görünür `<time datetime>` + kaynak satırı, (c) JSON-LD'de `dateModified`. Her build'de `new Date()` **yazma** — sinyali değersizleştirir.
- [ ] **T10 · Bölüm sayfalarını derinleştir** (L). Sayfanın başına veriden türetilmiş 2–3 cümlelik olgusal özet ("2026'da Hukuk 87 üniversitede açıldı; taban sıralaması 253 ile 148.000 arasında değişti; devlet üniversitelerinde medyan 41.200"). Ardından: 5 yıllık trend **sayısal tablo** olarak, devlet/vakıf ve il dağılımı, en çok yükselen/düşen programlar, net ortalamaları (T11), ilgili rehberlere ve sıralama bantlarına iç bağlantı. `<table>`'lara `<caption>` ve `<th scope>` ekle. `ItemList` JSON-LD ekle. Verisi eksik sayfaları `noindex` yap ya da sitemap dışında bırak.
- [ ] **T11 · Net verisini sunucu tarafında HTML'e al** (M). Modal kalsın, ama net ortalamaları bölüm sayfasında (ya da `/bolum/[slug]/netler` altında) düz HTML tablo olarak da yer alsın. Rakiplerde olmayan özgün veri.
- [ ] **T12 · Sıralama bandı sayfaları** (L). `/siralama/[puan-turu]/[bant]` — "100.000–150.000 sayısal sıralamayla gelen bölümler". Girişte tek cümlelik özet, hayal/dengeli/garanti ayrımı **metin etiketiyle** (renk tek başına anlam taşımaz), 3 yıllık trend, `ItemList`, sihirbaza CTA. ~50–100 sayfa; sorgu varyasyonu başına ayrı sayfa **açma** (Google bunu ölçekli içerik istismarıyla ilişkilendiriyor). Rakip: kocasor, eleman.net.
- [ ] **T13 · "Tercih robotu" landing sayfası** (M). `/tercih-robotu`'ndaki 301'i kaldır, gerçek bir sayfa yap: tercih robotu nedir, YÖK Atlas sihirbazından farkı, KolayTercih'in yöntemi, SSS. Mevcut `tercih-robotu-nedir-yok-atlas-nasil-kullanilir` rehberiyle kanibalize etmeyecek şekilde konumla.
- [ ] **T14 · Güven katmanı** (M).
- `/hakkinda`: kim yapıyor, şirket bilgisi, iletişim.
- Rehber frontmatter'ına `yazar` ve `guncelleme` alanları; sayfa gövdesinde görünür tarih ve yazar; `Article`'a `author` (`Person`) + `dateModified`. İdeal: bir rehber öğretmen ya da PDR uzmanının gözden geçirmesi ve "gözden geçiren" imzası.
- `/meraklisina`'ya hayal/dengeli/garanti eşiklerinin **sayısal** tanımı, filtre gevşetme sırası ve bilinen sınırlılıklar (şema ile senkron).
- Veri kaynağı sayfası: hangi ÖSYM/YÖK dosyası, hangi yıl, güncelleme sıklığı, hata bildirimi.
- [ ] **T15 · IndexNow** (S). Anahtar dosyası + veri güncellemesi/yeni rehber sonrası yalnızca **değişen** URL'leri gönderen script. Her deploy'da tüm URL'leri gönderme (429 riski).
- [x] **T16 · robots.ts temizliği** (S) — *yapıldı 21.09.2026: açık AI bot grubu; noindex sayfalardaki disallow kaldırıldı (yalnızca `/api/` engelli).* AI arama ve kullanıcı-tetiklemeli botlar için açık izin grupları (niyeti belgelemek için; bot kendi grubunu bulursa `*` grubunu yok sayar, bu yüzden `Disallow` satırları her grupta tekrar edilmeli). `/listem`, `/odeme/`, `/rapor/`, `/giris` için disallow ile noindex'ten birini seç (E10).
- [ ] **T17 · Bot takibi** (M). Traefik erişim logunu JSON olarak aç; haftalık: bot başına istek, en çok çekilen URL'ler, 4xx/429 oranı. `ChatGPT-User`, `Claude-User`, `Perplexity-User` isabetleri "birisi şu an bu sayfayı bir AI'a sordu" demek — alıntıya en yakın ölçüm.
### P2 — 1–3 ay (Kasım 2026–Ocak 2027: içerik kümeleri ve site dışı)
- [ ] **T18 · Yıllık veri raporu: "2026 YKS taban sıralama değişim raporu"** (L). En çok yükselen/düşen bölümler, boş kontenjan analizi (114.808 boş kontenjan; en çok bilgisayar mühendisliği), vakıf–devlet doluluk, yeni bölümler. Metodoloji + indirilebilir CSV + `Dataset` JSON-LD. **Hiçbir rakipte yok; basın yılda iki kez tam bu rakamları arıyor.** Bu, site dışı planın yakıtı.
- [ ] **T19 · Raporu servis et** (M). Eğitim muhabirleri, Webrazzi, yerel haber siteleri; eğitim YouTube kanallarına veri + demo. Hedef: marka adıyla anılma ve bağlantı.
- [ ] **T20 · Sözlük** (M). `/sozluk/[terim]`: başarı sırası, yerleştirme puanı, OBP, kırık OBP, ek puan, kontenjan türleri, TYÇ, özel koşul, ölü tercih, taban–tavan. Her terim: 40–60 kelimelik net tanım (ilk cümle), bir örnek, ÖSYM kılavuz atfı. `DefinedTerm` JSON-LD.
- [ ] **T21 · Karşılaştırma sayfaları** (L). Önce bölüm–bölüm (en çok aranan ~30 çift), sonra maliyet hesaplı "%50 burslu X mi, devlet Y mi". Tablo + gerçek veri; **öznel sıralama yok** (YMYL ve hukuki risk).
- [ ] **T22 · "Yapay zekâ ile tercih yapılır mı?" sayfası** (M). Rakip çok az. Genel amaçlı sohbet botlarının taban puan uydurabildiğini **kendi testimizle** belgele (ekran görüntüsü + tarih), havuz kısıtının bunu nasıl önlediğini anlat. Hem araç arama niyetine hem ürün farkına hizmet ediyor.
- [ ] **T23 · Rehber URL'lerinden yılı çıkar** (S). 5 rehberi kalıcı slug'lara taşı + 301; yıl başlıkta ve içerikte kalsın (E11).
- [ ] **T24 · Navigasyon ve semantik** (S–M). Header'a Bölümler / Üniversiteler / Rehber bağlantıları; footer kolon başlıklarını `<h2>`'den çıkar; `/bolumler` ve `/universiteler`'e giriş paragrafı; h1'e ya da hemen altına hizmeti adlandıran bir ifade (E12, E13). *Header değişikliği tasarım kararı — önce onay.*
- [ ] **T25 · Schema tamamlama** (S). `Organization.sameAs` (T27 profilleri açıldıktan sonra), bölümde `EducationalOccupationalProgram`, `/paket`'te `Offer`. Düşük beklenti; varlık netliği için.
- [ ] **T26 · Geriye dönük doğruluk testi** (L). "2025 verisiyle kurulan listeler 2026 yerleştirmesinde nasıl sonuç verirdi?" — garanti etiketli tercihlerin yüzde kaçı gerçekten tuttu. T8'den sonra mümkün. Hem güven içeriği hem basına verilebilir özgün bulgu.
- [ ] **T27 · Tutarlı marka profilleri** (S). LinkedIn şirket sayfası, YouTube kanalı, X — hepsinde aynı ad ("KolayTercih") ve aynı tek cümlelik tanım. Wikidata kaydı değerlendirilebilir; Wikipedia maddesi **zorlanmamalı** (kayda değerlik kriteri karşılanmaz, silinir).
- [ ] **T28 · Üçüncü taraf listeler** (M, sürekli). T7'de çıkan, LLM'lerin "en iyi tercih robotu" sorularında alıntıladığı Türkçe liste ve rehber sayfalarına ulaşıp eklenmeyi iste. DR<30 siteler de alıntılanıyor.
- [ ] **T29 · Topluluklar** (sürekli). r/YKS, Ekşi Sözlük, Technopat, DonanımHaber'de kurucu kimliğiyle, **şeffaf ve tanıtımsız**, soru cevaplayan katılım. Sahte anılma hem riskli hem etkisiz; Reddit 2026'da günde 25.000 spam içerik kaldırıyor. [A]
### P3 — Düşük öncelik / sigorta
- [x] **T30 · `htmlLimitedBots`** (S) — *yapıldı 21.09.2026.* Varsayılan listeyi koruyup AI botlarını ekleyen regex (§5.2 notu). Şu an etkisi yok; ileride dinamik sayfa eklenirse korur.
- [ ] **T31 · llms.txt** (S). `src/app/llms.txt/route.ts`, veriden üretilen (elle güncellenmeyen) Markdown; bağlantılar kanonik HTML sayfalarına. Rakipler eklemiş, etkisi kanıtlanmamış. Yazmadan önce `node_modules/next/dist/docs/` altındaki route handler dokümanını oku (`cacheComponents` açık).
- [ ] **T32 · Sayfalama URL'lerini sitemap'e ekle** (S). 667 self-canonical sayfa.
- [ ] **T33 · Rehberlerin `.md` sunumu** (S). Kaynak zaten Markdown. Bot user-agent'ına göre farklı içerik **sunma** (cloaking riski); yalnızca ayrı URL ya da `Accept` başlığıyla.
- [ ] **T34 · Logoları WebP/AVIF'e çevir, ana sayfa HTML boyutunu (~386 KB) incele** (M).
### Tercih sezonu 2027 için (takvime bağlı)
- [ ] ÖSYM 2027 takvimini yayınlayınca (beklenti: Kasım başı) takvim rehberini aynı gün güncelle.
- [ ] Haziran sonu: "netlerimle sıralamam" içeriği ve aracı.
- [ ] Kılavuz çıkınca 24–48 saat içinde: yeni bölümler, kontenjan değişimleri, koşul değişiklikleri özeti.
- [ ] Tercih dönemi (~13 gün): tarih içeren sayfaların ilk cümlesi net cevap; güncelleme saati görünür. ÖSYM 2026'da sonucu bir gün erken açıkladı — aynı gün güncelleme şart.
- [ ] Ağustos ortası–Eylül: "yerleşemedim", ek tercih, boş kontenjan sayfaları; yıllık raporun yeni sayısı.
---
## 8. Ölçüm planı
### 8.1 Ücretsiz ölçüm yığını
| Kaynak | Ne verir |
|---|---|
| **Bing Webmaster Tools → AI Performance** | Copilot/Bing AI alıntı sayıları, sayfa bazında alıntılar, grounding sorguları, Citation Share (Haziran 2026) |
| **Search Console → Generative AI performans raporu** | AI Bakışı / AI Modu gösterimleri (31 Ağustos 2026'da dünya geneline açıldı). Tıklama ve sorgu verisi yok. |
| **Rybbit** | AI referrer segmenti (T6) + kayıt/ödeme dönüşümü |
| **Search Console** | Markalı arama hacmi trendi ("kolaytercih", "kolay tercih") |
| **Traefik logu** | Bot isabetleri (T17) |
| **Kendi prompt koşumuz** | Anılma oranı, alıntı oranı, ses payı (aşağıda) |
Ücretli araç gerekirse en ucuzları: Otterly Lite ($29/ay), Rankscale (~$20/ay), Knowatoa ücretsiz plan. [A] Profound, Ahrefs Brand Radar ve Semrush bu aşamada gereksiz.
### 8.2 Kendi prompt takibimiz
**Set:** 40 sabit Türkçe prompt, 4 kova. Seti dondur; ekleme yaparsan ayrı versiyonla.
| Kova | Örnekler |
|---|---|
| Marka (5) | "KolayTercih nedir, güvenilir mi?" · "kolaytercih.com nasıl çalışıyor?" |
| Kategori / keşif (15) | "En iyi YKS tercih robotu hangisi?" · "Yapay zekâ destekli tercih danışmanlığı var mı?" · "Tercih listemi hazırlamama yardım edecek bir site öner." |
| Problem (15) | "Sayısalda sıralamam 150 bin, hangi bölümlere girebilirim?" · "24 tercihi nasıl dağıtmalıyım?" · "Ölü tercih nedir?" · "Hukuk taban sıralamaları 2026" |
| Karşılaştırma (5) | "YÖK Atlas tercih sihirbazı mı, <rakip> mi?" · "Ücretli tercih danışmanlığı mı, tercih robotu mu?" |
**Kurallar:**
- Aynı prompt iki kez sorulduğunda aynı listenin gelme olasılığı 1/100'den düşük. [D] "AI'da kaçıncı sıradayım" metriği anlamsız; anlamlı olan **çok koşuda görünme sıklığı**. Prompt başına en az 5 koşu, iki haftada bir.
- Web aramasını **zorlama**; doğal tetiklenme oranını da kaydet. Zorlamak görünürlüğü ortalama 20 puan saptırıyor. [D]
- API sonuçları arayüzdekinden farklı. Otomasyon için API (proje zaten OpenRouter kullanıyor; web eklentili modeller tek kapıdan ölçüm sağlar), ayda bir 10 promptluk **elle arayüz** kalibrasyonu.
- Trendleri 4 haftalık hareketli ortalamayla oku; platform kaynaklı sıçramalar tek haftayı anlamsız kılar.
**Kayıt alanları:** tarih · platform · model · prompt_id · koşu_no · arama_tetiklendi_mi · marka_anıldı_mı · rakip_anılmaları · alıntılanan_URL'ler · kendi_alan_adımız_alıntılandı_mı · duygu · bilgi_doğru_mu.
### 8.3 KPI'lar
| Katman | Metrik |
|---|---|
| Görünürlük | **Anılma oranı** (anılan koşu / toplam) · **alıntı oranı** (alan adımızın alıntılandığı koşu / arama tetiklenen koşu) · **ses payı** (biz / biz + rakipler) · hedef üçüncü taraf sayfalarda varlık |
| Kalite | AI fiyatı, yöntemi ve "garanti yok" beyanını doğru anlatıyor mu · duygu |
| Birinci taraf | Search Console AI gösterimleri · Bing AI alıntıları · bot isabetleri |
| İş | AI referrer oturumları ve dönüşüm · markalı arama hacmi · kayıt formunda "bizi nereden duydun: ChatGPT/yapay zekâ" seçeneği |
**Gerçekçi ilk hedefler (Temmuz 2027'ye kadar)** [Ç]: marka sorgularında %100 doğru yanıt; problem kovasında arama tetiklenen koşularda alıntı oranı > %10; kategori kovasında anılma oranı > 0 (yeni marka için bu bile kazanım); Bing AI Performance'ta aylık artan alıntı.
---
## 9. Yapılmayacaklar
| Taktik | Neden |
|---|---|
| Gizli metin, "AI için" talimat gömme, "AI ile özetle" butonlarıyla hafıza zehirleme | Microsoft bunu saldırı olarak sınıflandırdı (MITRE AML.T0080); itibar ve güvenlik riski, kalıcı kazanç yok |
| Kendimizi 1. sıraya koyan "en iyi tercih robotu" listesi | Alıntılandığında %69 vakada rakip öneriliyor; Google'ın bu tür sayfaları cezalandırdığına dair işaretler var [A] |
| LLM ile ölçekli dolgu metni üretip binlerce sayfaya basmak | Ağustos 2026 spam güncellemesi tam bunu vurdu; ürünün "veri olmadan tahmin yürütmez" ilkesiyle de çelişir |
| Sorgu varyasyonu başına ayrı sayfa ("X taban puanı" / "X sıralaması" / "X 2026") | Google'ın Mayıs 2026 rehberi bunu ölçekli içerik istismarıyla ilişkilendiriyor |
| İçerik değişmeden tarih güncellemek | Tazelik sinyalini sahteler; önerilmiyor |
| Forumlarda gizli tanıtım, sahte yorum | Etkisiz ve riskli; şeffaf kurucu katılımı işe yarıyor |
| Bot user-agent'ına göre farklı içerik sunmak | Cloaking |
| Schema'dan ya da llms.txt'den alıntı artışı beklemek | Kontrollü çalışmalarda etki ~sıfır |
| `nosnippet` ya da düşük `max-snippet` | AI Bakışı'na girdiyi de engeller |
| Tek platforma (ör. Reddit) yatırım | Paylar haftalar içinde çöküyor |
---
## 10. Doğrulanamayanlar ve açık sorular
- LLM'lerin Türkçe tercih sorgularında **şu an** hangi kaynakları alıntıladığı test edilemedi → T7.
- YÖK Atlas ve ÖSYM verisinin ticari kullanım koşulları bulunamadı → hukuki teyit.
- 2027 YKS resmî tarihleri henüz yok.
- Ekşi Sözlük / Technopat / DonanımHaber'in Türkçe LLM yanıtlarındaki payı hakkında veri yok.
- Yazar kutusunun AI alıntısını artırdığını doğrudan ölçen sağlam çalışma bulunamadı (dolaylı kanıt var).
- "ChatGPT trafiği organikten X kat iyi dönüştürüyor" türü rakamların birincil kaynakları doğrulanamadı; bağımsız çalışmada fark anlamsız.
- Cloudflare: alan adı Cloudflare nameserver'larını kullanıyor ama proxy kapalı. **Proxy açılırsa** AI bot engeli ve "managed robots.txt" ayarları hemen kontrol edilmeli (yeni zone'larda AI eğitim botları varsayılan olarak engelleniyor).
---
## 11. Kaynaklar
**Akademik**
- Aggarwal ve ark., *GEO: Generative Engine Optimization*, KDD 2024 — https://arxiv.org/abs/2311.09735
- Eleştirel derleme, Temmuz 2026 — https://arxiv.org/html/2607.14035v1
- C-SEO Bench, NeurIPS 2025 — https://arxiv.org/abs/2506.11097
- Chen ve ark., *How to Dominate AI Search* — https://arxiv.org/abs/2509.08919
- GEO-16 — https://arxiv.org/abs/2509.10762
**Google / Bing / sağlayıcı dokümanları**
- Google AI optimizasyon rehberi — https://developers.google.com/search/docs/fundamentals/ai-optimization-guide
- Google "AI features and your website" — https://developers.google.com/search/docs/appearance/ai-features
- Google robots meta / snippet kontrolleri — https://developers.google.com/search/docs/crawling-indexing/robots-meta-tag
- Google spam politikaları — https://developers.google.com/search/docs/essentials/spam-policies
- Search Console Generative AI raporu — https://developers.google.com/search/blog/2026/06/gen-ai-performance-reports
- Bing AI Performance — https://blogs.bing.com/webmaster/February-2026/Introducing-AI-Performance-in-Bing-Webmaster-Tools-Public-Preview
- Bing: sitemap ve lastmod — https://blogs.bing.com/webmaster/July-2025/Keeping-Content-Discoverable-with-Sitemaps-in-AI-Powered-Search
- IndexNow — https://www.indexnow.org/documentation
- OpenAI botları — https://developers.openai.com/api/docs/bots
- Anthropic botları — https://support.claude.com/en/articles/8896518
- Perplexity botları — https://docs.perplexity.ai/guides/bots
- llms.txt spesifikasyonu — https://llmstxt.org/
- Cloudflare AI bot engeli — https://developers.cloudflare.com/bots/additional-configurations/block-ai-bots/
**Sektör araştırmaları**
- Ahrefs, marka görünürlüğü korelasyonları — https://ahrefs.com/blog/ai-brand-visibility-correlations
- Ahrefs, tazelik — https://ahrefs.com/blog/do-ai-assistants-prefer-to-cite-fresh-content
- Ahrefs, "best" listeleri — https://ahrefs.com/blog/best-lists-research/
- Ahrefs, schema ve AI alıntıları — https://ahrefs.com/blog/schema-ai-citations/
- Ahrefs, AI Bakışı alıntıları ve ilk 10 — https://ahrefs.com/blog/ai-overview-citations-top-10
- Ahrefs, AI Bakışı ve tıklamalar — https://ahrefs.com/blog/ai-overviews-reduce-clicks-update
- Ahrefs, kendini öven içerik deneyi — https://ahrefs.com/blog/self-promotional-content-ai-seo-experiment/
- Surfer, sorgu yayılımı — https://surferseo.com/blog/query-fan-out-impact
- Vercel + MERJ, AI tarayıcıları ve JS — https://vercel.com/blog/the-rise-of-the-ai-crawler
- searchVIU, schema ve canlı fetch — https://www.searchviu.com/en/schema-markup-and-ai-in-2025-what-chatgpt-claude-perplexity-gemini-really-see/
- Peec AI, ChatGPT'nin kendi indeksi — https://peec.ai/blog/chatgpt-built-its-own-search-index
- Graphite, aramayı zorlamama — https://graphite.io/five-percent/research/do-not-force-search-prompt-tracking
- SparkToro, AI önerilerinin tutarsızlığı — https://sparktoro.com/blog/new-research-ais-are-highly-inconsistent-when-recommending-brands-or-products-marketers-should-take-care-when-tracking-ai-visibility/
- SparkToro, tıklamasız aramalar 2026 — https://sparktoro.com/blog/in-2026-less-than-one-third-of-google-searches-still-send-a-click/
- Profound, platform alıntı örüntüleri — https://www.tryprofound.com/blog/ai-platform-citation-patterns
- Profound, Reddit verisi — https://www.tryprofound.com/blog/the-data-on-reddit-and-ai-search
- Semrush, en çok alıntılanan alan adları — https://www.semrush.com/blog/most-cited-domains-ai/
- Goodie, sosyal medya alıntıları 2026 — https://higoodie.com/blog/social-media-ai-citations-study-2026/
- Similarweb, üretken AI istatistikleri — https://aisearch.similarweb.com/blog/gen-ai-stats/
- Conductor, AEO/GEO kıyaslamaları — https://www.conductor.com/academy/aeo-geo-benchmarks-report/
- Amsive, LLM trafiği dönüşümü — https://www.amsive.com/insights/seo/does-llm-traffic-convert-better-than-organic-a-new-data-backed-study/
- Seer Interactive, AI Bakışı ve tıklama oranı — https://www.seerinteractive.com/insights/aio-impact-on-google-ctr-2026-update
- Pew Research, AI özetleri ve tıklama — https://www.pewresearch.org/short-reads/2025/07/22/google-users-are-less-likely-to-click-on-links-when-an-ai-summary-appears-in-the-results/
- Microsoft, AI öneri zehirleme — https://www.microsoft.com/en-us/security/blog/2026/02/10/ai-recommendation-poisoning/
- SEJ, llms.txt ve 300 bin alan adı — https://www.searchenginejournal.com/llms-txt-shows-no-clear-effect-on-ai-citations-based-on-300k-domains/561542/
- SEJ, SSS zengin sonuçlarının kaldırılması — https://www.searchenginejournal.com/google-drops-faq-rich-results-from-search/574429/
- GSQi, Ağustos 2026 spam güncellemesi — https://www.gsqi.com/marketing-blog/august-2026-google-spam-update-case-studies/
**Türkiye**
- Smartkid, Türkiye AI Görünürlük Raporu 2026 — https://www.smartkid.agency/blog/turkiye-ai-gorunurluk-raporu-2026
- Stradiji, Türkçe sorgularda kaynak boşluğu — https://www.stradiji.com/in-english-g2-tells-ai-the-best-tool-in-turkish-that-seat-is-empty-and-ads-fill-it/
- Webrazzi, AI Modu ve AI Bakışı Türkiye'de — https://webrazzi.com/2026/02/18/google-ai-modu-ve-ai-bakisi-ozelliklerini-turkiye-de-de-kullanima-sunmaya-basladi/
**YKS / ÖSYM**
- 2026-YKS sonuçları — https://www.osym.gov.tr/2026yks-sinav-sonuclari-aciklandi
- 2026-YKS tercihlerin alınması — https://www.osym.gov.tr/2026-yks-tercihlerin-alinmasi
- 2026-YKS yerleştirme sonuçları — https://www.osym.gov.tr/2026-yks-yerlestirme-sonuclari-aciklandi
- Yerleştirme sayısal bilgileri (TABLO-3/4) — https://www.osym.gov.tr/2026-yks-yerlestirme-sonuclarina-iliskin-sayisal-bilgiler
- Ek yerleştirme — https://www.osym.gov.tr/2026-yks-ek-yerlestirme-tercihlerin-alinmasi
- Hürriyet, 24 tercih hakkı nasıl yönetilir — https://www.hurriyet.com.tr/egitim/24-tercih-hakki-nasil-yonetilir-olu-tercih-tuzagina-dusmeyin-43252774
- Hürriyet, ek yerleştirme ve boş kontenjanlar — https://www.hurriyet.com.tr/egitim/yksde-ek-yerlestirme-basladi-en-cok-bosluk-olan-bolumlere-dikkat-43310701
- CollegeVine, tahmin doğruluğu yazısı — https://blog.collegevine.com/is-collegevine-chancing-accurate
- Niche, sıralama metodolojisi — https://www.niche.com/colleges/rankings/methodology/

View File

@@ -1,11 +1,25 @@
import type { NextConfig } from "next"; import type { NextConfig } from "next";
import { HTML_LIMITED_BOT_UA_RE } from "next/dist/shared/lib/router/utils/html-bots";
// Dockerfile builder katmanı BUILD_KAYNAK_KISITLI=1 verir: imaj build'i prod // Dockerfile builder katmanı BUILD_KAYNAK_KISITLI=1 verir: imaj build'i prod
// VPS'te çalışan uygulamayla aynı makinede koştuğu için bellek/paralellik kısılır. // VPS'te çalışan uygulamayla aynı makinede koştuğu için bellek/paralellik kısılır.
// Lokal build ve dev bu kısıtlardan etkilenmez. // Lokal build ve dev bu kısıtlardan etkilenmez.
const kaynakKisitli = process.env.BUILD_KAYNAK_KISITLI === "1"; const kaynakKisitli = process.env.BUILD_KAYNAK_KISITLI === "1";
// Arama/AI botları JS çalıştırmaz. Next, metadata'sı istek anına kalan
// sayfalarda <title>/canonical/robots etiketlerini <body>'ye akıtır; yalnızca
// bu listedeki user-agent'lara <head> içinde, bloklayarak verir. Varsayılan
// listede AI botları yok. Şu an herkese açık sayfaların hepsi prerender
// edildiği için etkisi yok; ileride dinamik bir sayfa eklenirse sigortadır.
// `htmlLimitedBots` varsayılanı EZDİĞİ için Next'in kendi listesi de eklenir.
const AI_BOTLARI =
"GPTBot|OAI-SearchBot|ChatGPT-User|ClaudeBot|Claude-User|Claude-SearchBot|PerplexityBot|Perplexity-User|meta-externalagent|meta-externalfetcher|meta-webindexer|Amazonbot|Amzn-SearchBot|Amzn-User|DuckAssistBot|MistralAI-User|CCBot|Bytespider";
const nextConfig: NextConfig = { const nextConfig: NextConfig = {
htmlLimitedBots: new RegExp(
`${HTML_LIMITED_BOT_UA_RE.source}|${AI_BOTLARI}`,
"i",
),
// PPR: statik kabuk anında servis edilir, dinamik parçalar (UserNav vb.) // PPR: statik kabuk anında servis edilir, dinamik parçalar (UserNav vb.)
// Suspense sınırlarının içinde akar. Bkz. docs/cacheComponents.md. // Suspense sınırlarının içinde akar. Bkz. docs/cacheComponents.md.
cacheComponents: true, cacheComponents: true,

View File

@@ -1,18 +1,18 @@
import { Suspense } from "react"; import { Suspense } from "react";
import type { Metadata } from "next"; import type { Metadata } from "next";
import { JsonLd, breadcrumbJsonLd } from "@/lib/seo"; import { JsonLd, breadcrumbJsonLd, sayfaMetadata } from "@/lib/seo";
import { SiteFooter } from "@/components/site-footer"; import { SiteFooter } from "@/components/site-footer";
import { import {
BolumKatalogu, BolumKatalogu,
BolumKataloguSkeleton, BolumKataloguSkeleton,
} from "@/features/katalog/components/bolum-listesi"; } from "@/features/katalog/components/bolum-listesi";
export const metadata: Metadata = { export const metadata: Metadata = sayfaMetadata({
title: "Tüm Bölümlerin Taban Puanları ve Sıralamaları 2026", title: "Tüm Bölümlerin Taban Puanları ve Sıralamaları 2026",
description: description:
"Üniversite bölümlerinin 2025 yerleştirme taban puanları ve başarı sıralamaları: lisans ve önlisans programları kategori kategori, 2026 tercihleri için.", "Üniversite bölümlerinin 2025 yerleştirme taban puanları ve başarı sıralamaları: lisans ve önlisans programları kategori kategori, 2026 tercihleri için.",
alternates: { canonical: "/bolumler" }, path: "/bolumler",
}; });
export default function BolumlerPage() { export default function BolumlerPage() {
return ( return (

View File

@@ -3,13 +3,14 @@ import Link from "next/link";
import { Parallax } from "@/components/parallax"; import { Parallax } from "@/components/parallax";
import { PagePixelDivider } from "@/components/pixel-decor"; import { PagePixelDivider } from "@/components/pixel-decor";
import { SiteFooter } from "@/components/site-footer"; import { SiteFooter } from "@/components/site-footer";
import { sayfaMetadata } from "@/lib/seo";
export const metadata: Metadata = { export const metadata: Metadata = sayfaMetadata({
title: "Gizlilik ve KVKK", title: "Gizlilik ve KVKK",
description: description:
"KolayTercih gizlilik politikası ve KVKK aydınlatma metni: hangi veriler toplanır, nasıl işlenir ve haklarınız nelerdir.", "KolayTercih gizlilik politikası ve KVKK aydınlatma metni: hangi veriler toplanır, nasıl işlenir ve haklarınız nelerdir.",
alternates: { canonical: "/gizlilik" }, path: "/gizlilik",
}; });
// NOT: MVP taslağıdır; yayına almadan önce hukuki gözden geçirme gerekir. // NOT: MVP taslağıdır; yayına almadan önce hukuki gözden geçirme gerekir.
export default function GizlilikPage() { export default function GizlilikPage() {

View File

@@ -3,13 +3,14 @@ import { Mail } from "lucide-react";
import { Parallax } from "@/components/parallax"; import { Parallax } from "@/components/parallax";
import { PagePixelDivider } from "@/components/pixel-decor"; import { PagePixelDivider } from "@/components/pixel-decor";
import { SiteFooter } from "@/components/site-footer"; import { SiteFooter } from "@/components/site-footer";
import { sayfaMetadata } from "@/lib/seo";
export const metadata: Metadata = { export const metadata: Metadata = sayfaMetadata({
title: "İletişim", title: "İletişim",
description: description:
"KolayTercih destek ekibine ulaş: sorular, geri bildirim ve iade talepleri için iletişim kanalları.", "KolayTercih destek ekibine ulaş: sorular, geri bildirim ve iade talepleri için iletişim kanalları.",
alternates: { canonical: "/iletisim" }, path: "/iletisim",
}; });
export default function IletisimPage() { export default function IletisimPage() {
return ( return (

View File

@@ -3,13 +3,14 @@ import Link from "next/link";
import { Parallax } from "@/components/parallax"; import { Parallax } from "@/components/parallax";
import { PagePixelDivider } from "@/components/pixel-decor"; import { PagePixelDivider } from "@/components/pixel-decor";
import { SiteFooter } from "@/components/site-footer"; import { SiteFooter } from "@/components/site-footer";
import { sayfaMetadata } from "@/lib/seo";
export const metadata: Metadata = { export const metadata: Metadata = sayfaMetadata({
title: "Kullanım ve Satış Koşulları", title: "Kullanım ve Satış Koşulları",
description: description:
"KolayTercih kullanım ve satış koşulları: hizmet kapsamı, ödeme, iade politikası ve sorumluluk sınırları.", "KolayTercih kullanım ve satış koşulları: hizmet kapsamı, ödeme, iade politikası ve sorumluluk sınırları.",
alternates: { canonical: "/kosullar" }, path: "/kosullar",
}; });
// NOT: MVP taslağıdır; yayına almadan önce hukuki gözden geçirme gerekir. // NOT: MVP taslağıdır; yayına almadan önce hukuki gözden geçirme gerekir.
export default function KosullarPage() { export default function KosullarPage() {

View File

@@ -7,13 +7,14 @@ import {
MeraklisinaIcerik, MeraklisinaIcerik,
MeraklisinaIcerikSkeleton, MeraklisinaIcerikSkeleton,
} from "@/features/pazarlama/components/meraklisina-icerik"; } from "@/features/pazarlama/components/meraklisina-icerik";
import { sayfaMetadata } from "@/lib/seo";
export const metadata: Metadata = { export const metadata: Metadata = sayfaMetadata({
title: "Meraklısına: KolayTercih nasıl çalışır?", title: "Meraklısına: KolayTercih nasıl çalışır?",
alternates: { canonical: "/meraklisina" }, path: "/meraklisina",
description: description:
"Sıralamanı girdiğinde arka planda ne oluyor? Dilimler, risk renkleri ve sihirbazın adım adım daralan filtreleri — öğrenci diliyle, sıralamana göre hesaplanan canlı görsellerle.", "Sıralamanı girdiğinde arka planda ne oluyor? Dilimler, risk renkleri ve sihirbazın adım adım daralan filtreleri — öğrenci diliyle, sıralamana göre hesaplanan canlı görsellerle.",
}; });
export default function MeraklisinaPage() { export default function MeraklisinaPage() {
return ( return (

View File

@@ -8,13 +8,14 @@ import {
PaketSatinAl, PaketSatinAl,
PaketSatinAlSkeleton, PaketSatinAlSkeleton,
} from "@/features/odeme/components/paket-satinal"; } from "@/features/odeme/components/paket-satinal";
import { sayfaMetadata } from "@/lib/seo";
export const metadata: Metadata = { export const metadata: Metadata = sayfaMetadata({
title: "Tercih Dönemi Paketi", title: "Tercih Dönemi Paketi",
description: description:
"Tek seferlik Tercih Dönemi Paketi: yapay zekâ destekli kişisel 24 tercihlik liste, risk analizi, liste revizyonları ve soru hakkı. Abonelik yok.", "Tek seferlik Tercih Dönemi Paketi: yapay zekâ destekli kişisel 24 tercihlik liste, risk analizi, liste revizyonları ve soru hakkı. Abonelik yok.",
alternates: { canonical: "/paket" }, path: "/paket",
}; });
export default function PaketPage({ export default function PaketPage({
searchParams, searchParams,

View File

@@ -1,6 +1,6 @@
import { Suspense } from "react"; import { Suspense } from "react";
import type { Metadata } from "next"; import type { Metadata } from "next";
import { JsonLd, breadcrumbJsonLd } from "@/lib/seo"; import { JsonLd, breadcrumbJsonLd, sayfaMetadata } from "@/lib/seo";
import { CtaSiraForm } from "@/features/sihirbaz/components/cta-sira-form"; import { CtaSiraForm } from "@/features/sihirbaz/components/cta-sira-form";
import { SiteFooter } from "@/components/site-footer"; import { SiteFooter } from "@/components/site-footer";
import { PagePixelDivider } from "@/components/pixel-decor"; import { PagePixelDivider } from "@/components/pixel-decor";
@@ -9,12 +9,12 @@ import {
RehberListesiSkeleton, RehberListesiSkeleton,
} from "@/features/rehber/components/rehber-listesi"; } from "@/features/rehber/components/rehber-listesi";
export const metadata: Metadata = { export const metadata: Metadata = sayfaMetadata({
title: "Tercih Rehberi 2026 — YKS Tercih Dönemi Yazıları", title: "Tercih Rehberi 2026 — YKS Tercih Dönemi Yazıları",
description: description:
"YKS tercih dönemi rehberi: tercih listesi nasıl yapılır, ölü tercih nedir, ek yerleştirme nasıl işler, sıralama mı puan mı — hepsi öğrenci diliyle.", "YKS tercih dönemi rehberi: tercih listesi nasıl yapılır, ölü tercih nedir, ek yerleştirme nasıl işler, sıralama mı puan mı — hepsi öğrenci diliyle.",
alternates: { canonical: "/rehber" }, path: "/rehber",
}; });
export default function RehberIndexPage() { export default function RehberIndexPage() {
return ( return (

View File

@@ -1,15 +1,44 @@
import type { MetadataRoute } from "next"; import type { MetadataRoute } from "next";
import { SITE_URL } from "@/lib/seo"; import { SITE_URL } from "@/lib/seo";
// Yalnızca /api/ engellenir. /sonuc, /giris, /listem, /odeme/*, /rapor/*
// bilinçli olarak ENGELLENMEDİ: bu sayfalar noindex meta taşıyor ve robots.txt
// engeli o meta'nın görülmesini imkânsız kılardı (dış link alırlarsa
// "robots.txt tarafından engellendi ama dizine eklendi" durumuna düşerlerdi).
const DISALLOW = ["/api/"];
// Arama/cevap motorlarının tarayıcıları. `*` kuralı bunları zaten kapsıyor;
// grup, niyeti belgelemek için açıkça yazıldı: AI aramalarında kaynak
// gösterilmek istiyoruz. DİKKAT: bir bot kendi adına yazılmış grubu bulursa `*`
// grubunu yok sayar — buraya eklenen her bot için DISALLOW tekrar edilmeli.
const AI_BOTLARI = [
// Arama indeksi + kullanıcı tetiklemeli fetch
"OAI-SearchBot",
"ChatGPT-User",
"Claude-SearchBot",
"Claude-User",
"PerplexityBot",
"Perplexity-User",
"DuckAssistBot",
"MistralAI-User",
"meta-webindexer",
"Amzn-SearchBot",
// Model eğitimi: aramasız yanıtlarda markanın bilinmesi buradan geçer
"GPTBot",
"ClaudeBot",
"Google-Extended",
"Applebot-Extended",
"meta-externalagent",
"Amazonbot",
"CCBot",
];
export default function robots(): MetadataRoute.Robots { export default function robots(): MetadataRoute.Robots {
return { return {
rules: { rules: [
userAgent: "*", { userAgent: "*", allow: "/", disallow: DISALLOW },
allow: "/", { userAgent: AI_BOTLARI, allow: "/", disallow: DISALLOW },
// /sonuc bilinçli olarak ENGELLENMEDİ: sayfa noindex meta taşıyor ve ],
// robots.txt engeli o meta'nın görülmesini imkânsız kılardı.
disallow: ["/api/", "/listem", "/odeme/", "/rapor/", "/giris"],
},
sitemap: `${SITE_URL}/sitemap.xml`, sitemap: `${SITE_URL}/sitemap.xml`,
}; };
} }

View File

@@ -1,5 +1,5 @@
import type { MetadataRoute } from "next"; import type { MetadataRoute } from "next";
import { SITE_URL } from "@/lib/seo"; import { KATALOG_VERI_TARIHI, SITE_URL } from "@/lib/seo";
import { getAllBolumSlugs, getAllUniversiteSlugs } from "@/lib/katalog"; import { getAllBolumSlugs, getAllUniversiteSlugs } from "@/lib/katalog";
import { getAllRehberler } from "@/lib/rehber"; import { getAllRehberler } from "@/lib/rehber";
@@ -7,8 +7,18 @@ import { getAllRehberler } from "@/lib/rehber";
// /odeme/sonuc, /rapor/yazdir (auth/utility sayfaları). // /odeme/sonuc, /rapor/yazdir (auth/utility sayfaları).
const staticEntries: MetadataRoute.Sitemap = [ const staticEntries: MetadataRoute.Sitemap = [
{ url: `${SITE_URL}/`, priority: 1, changeFrequency: "weekly" }, { url: `${SITE_URL}/`, priority: 1, changeFrequency: "weekly" },
{ url: `${SITE_URL}/bolumler`, priority: 0.8, changeFrequency: "weekly" }, {
{ url: `${SITE_URL}/universiteler`, priority: 0.8, changeFrequency: "weekly" }, url: `${SITE_URL}/bolumler`,
lastModified: KATALOG_VERI_TARIHI,
priority: 0.8,
changeFrequency: "weekly",
},
{
url: `${SITE_URL}/universiteler`,
lastModified: KATALOG_VERI_TARIHI,
priority: 0.8,
changeFrequency: "weekly",
},
{ url: `${SITE_URL}/rehber`, priority: 0.8, changeFrequency: "weekly" }, { url: `${SITE_URL}/rehber`, priority: 0.8, changeFrequency: "weekly" },
{ url: `${SITE_URL}/paket`, priority: 0.7, changeFrequency: "monthly" }, { url: `${SITE_URL}/paket`, priority: 0.7, changeFrequency: "monthly" },
{ url: `${SITE_URL}/meraklisina`, priority: 0.6, changeFrequency: "monthly" }, { url: `${SITE_URL}/meraklisina`, priority: 0.6, changeFrequency: "monthly" },
@@ -20,12 +30,14 @@ const staticEntries: MetadataRoute.Sitemap = [
export default async function sitemap(): Promise<MetadataRoute.Sitemap> { export default async function sitemap(): Promise<MetadataRoute.Sitemap> {
const bolumler: MetadataRoute.Sitemap = getAllBolumSlugs().map((slug) => ({ const bolumler: MetadataRoute.Sitemap = getAllBolumSlugs().map((slug) => ({
url: `${SITE_URL}/bolum/${slug}`, url: `${SITE_URL}/bolum/${slug}`,
lastModified: KATALOG_VERI_TARIHI,
priority: 0.7, priority: 0.7,
changeFrequency: "monthly", changeFrequency: "monthly",
})); }));
const universiteler: MetadataRoute.Sitemap = getAllUniversiteSlugs().map( const universiteler: MetadataRoute.Sitemap = getAllUniversiteSlugs().map(
(slug) => ({ (slug) => ({
url: `${SITE_URL}/universite/${slug}`, url: `${SITE_URL}/universite/${slug}`,
lastModified: KATALOG_VERI_TARIHI,
priority: 0.6, priority: 0.6,
changeFrequency: "monthly", changeFrequency: "monthly",
}), }),

View File

@@ -1,18 +1,18 @@
import { Suspense } from "react"; import { Suspense } from "react";
import type { Metadata } from "next"; import type { Metadata } from "next";
import { JsonLd, breadcrumbJsonLd } from "@/lib/seo"; import { JsonLd, breadcrumbJsonLd, sayfaMetadata } from "@/lib/seo";
import { SiteFooter } from "@/components/site-footer"; import { SiteFooter } from "@/components/site-footer";
import { import {
UniversiteKatalogu, UniversiteKatalogu,
UniversiteKataloguSkeleton, UniversiteKataloguSkeleton,
} from "@/features/katalog/components/universite-listesi"; } from "@/features/katalog/components/universite-listesi";
export const metadata: Metadata = { export const metadata: Metadata = sayfaMetadata({
title: "Üniversitelerin Taban Puanları ve Sıralamaları 2026", title: "Üniversitelerin Taban Puanları ve Sıralamaları 2026",
description: description:
"Devlet ve vakıf üniversitelerinin 2025 yerleştirme taban puanları, başarı sıralamaları ve kontenjanları — üniversite üniversite, 2026 tercihleri için.", "Devlet ve vakıf üniversitelerinin 2025 yerleştirme taban puanları, başarı sıralamaları ve kontenjanları — üniversite üniversite, 2026 tercihleri için.",
alternates: { canonical: "/universiteler" }, path: "/universiteler",
}; });
export default function UniversitelerPage() { export default function UniversitelerPage() {
return ( return (

View File

@@ -60,19 +60,31 @@ function AccordionContent({
children, children,
...props ...props
}: React.ComponentProps<typeof AccordionPrimitive.Content>) { }: React.ComponentProps<typeof AccordionPrimitive.Content>) {
// forceMount: kapalı içerik de ilk HTML'de kalır — JS çalıştırmayan
// tarayıcılar (arama/AI botları) cevapları okuyabilsin. Radix kapalıyken
// içeriği hiç render etmez; forceMount ile de yükseklik ölçümü kapalıyken 0
// çıktığı için accordion-up/down keyframe'leri çalışmaz. Aç/kapa bu yüzden
// grid-rows geçişiyle yapılır; geçiş Content'in KENDİSİNDE değil çocuğunda
// durur, çünkü Radix ölçüm sırasında Content'in transition-duration'ını
// sıfırlar. visibility geçişi kapalı içeriği odak/ekran okuyucudan çıkarır.
return ( return (
<AccordionPrimitive.Content <AccordionPrimitive.Content
forceMount
data-slot="accordion-content" data-slot="accordion-content"
className="overflow-hidden text-sm data-open:animate-accordion-down data-closed:animate-accordion-up" className="group/accordion-content text-sm"
{...props} {...props}
> >
<div <div className="grid grid-rows-[1fr] transition-[grid-template-rows,visibility] duration-200 ease-out group-data-[state=closed]/accordion-content:invisible group-data-[state=closed]/accordion-content:grid-rows-[0fr] motion-reduce:transition-none">
className={cn( <div className="min-h-0 overflow-hidden">
"h-(--radix-accordion-content-height) pt-0 pb-2.5 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4", <div
className className={cn(
)} "pt-0 pb-2.5 [&_a]:underline [&_a]:underline-offset-3 [&_a]:hover:text-foreground [&_p:not(:last-child)]:mb-4",
> className
{children} )}
>
{children}
</div>
</div>
</div> </div>
</AccordionPrimitive.Content> </AccordionPrimitive.Content>
) )

View File

@@ -8,9 +8,15 @@ import {
getBolumBySlug, getBolumBySlug,
uniSlugFromAd, uniSlugFromAd,
} from "../katalog-queries"; } from "../katalog-queries";
import { JsonLd, breadcrumbJsonLd } from "@/lib/seo"; import {
JsonLd,
breadcrumbJsonLd,
sayfaMetadata,
ustSegmentOgGorseli,
} from "@/lib/seo";
import { SAYFA_BOYU } from "./sayfalama"; import { SAYFA_BOYU } from "./sayfalama";
import { BolumProgramListesi } from "./bolum-program-listesi"; import { BolumProgramListesi } from "./bolum-program-listesi";
import { VeriKaynagi } from "./veri-kaynagi";
import { SiteFooter } from "@/components/site-footer"; import { SiteFooter } from "@/components/site-footer";
import { Skeleton } from "@/components/ui/skeleton"; import { Skeleton } from "@/components/ui/skeleton";
@@ -41,21 +47,27 @@ export function bolumSayfaMetadata(slug: string, sayfa: number): Metadata {
if (!bolum) return {}; if (!bolum) return {};
const { stats } = bolum; const { stats } = bolum;
if (sayfa > 1) { if (sayfa > 1) {
return { return sayfaMetadata({
title: `${bolum.ad} Taban Puanları 2026 (Sayfa ${sayfa})`, title: `${bolum.ad} Taban Puanları 2026 (Sayfa ${sayfa})`,
description: `${bolum.ad} taban puanları ve başarı sıralamaları tablosunun ${sayfa}. sayfası — toplam ${stats.toplam} programın 2025 yerleştirme verileri.`, description: `${bolum.ad} taban puanları ve başarı sıralamaları tablosunun ${sayfa}. sayfası — toplam ${stats.toplam} programın 2025 yerleştirme verileri.`,
alternates: { canonical: `/bolum/${slug}/sayfa/${sayfa}` }, path: `/bolum/${slug}/sayfa/${sayfa}`,
}; openGraph: {
images: ustSegmentOgGorseli(
`/bolum/${slug}`,
"Bölüm taban puanları ve sıralamaları — KolayTercih",
),
},
});
} }
const enIyi = const enIyi =
stats.minSira != null stats.minSira != null
? ` En iyi taban sıralaması ${sayi(stats.minSira)}.` ? ` En iyi taban sıralaması ${sayi(stats.minSira)}.`
: ""; : "";
return { return sayfaMetadata({
title: `${bolum.ad} Taban Puanları ve Başarı Sıralamaları 2026`, title: `${bolum.ad} Taban Puanları ve Başarı Sıralamaları 2026`,
description: `${bolum.ad} 2025 yerleştirme verileri: ${stats.toplam} programın taban puanı ve sıralaması, ${stats.devlet} devlet / ${stats.vakif} vakıf.${enIyi} 2026 tercih rehberi.`, description: `${bolum.ad} 2025 yerleştirme verileri: ${stats.toplam} programın taban puanı ve sıralaması, ${stats.devlet} devlet / ${stats.vakif} vakıf.${enIyi} 2026 tercih rehberi.`,
alternates: { canonical: `/bolum/${slug}` }, path: `/bolum/${slug}`,
}; });
} }
export function BolumIcerik({ slug, sayfa }: { slug: string; sayfa: number }) { export function BolumIcerik({ slug, sayfa }: { slug: string; sayfa: number }) {
@@ -139,6 +151,7 @@ export function BolumIcerik({ slug, sayfa }: { slug: string; sayfa: number }) {
tabanYol={`/bolum/${slug}`} tabanYol={`/bolum/${slug}`}
sayfa={sayfa} sayfa={sayfa}
/> />
<VeriKaynagi className="mt-6" />
</section> </section>
</main> </main>
<SiteFooter /> <SiteFooter />

View File

@@ -9,7 +9,13 @@ import {
bolumSlugFromIsim, bolumSlugFromIsim,
} from "../katalog-queries"; } from "../katalog-queries";
import { trBaslikDuzeni } from "@/lib/slug"; import { trBaslikDuzeni } from "@/lib/slug";
import { JsonLd, breadcrumbJsonLd, SITE_URL } from "@/lib/seo"; import {
JsonLd,
breadcrumbJsonLd,
sayfaMetadata,
SITE_URL,
ustSegmentOgGorseli,
} from "@/lib/seo";
import { CtaSiraForm } from "@/features/sihirbaz/components/cta-sira-form"; import { CtaSiraForm } from "@/features/sihirbaz/components/cta-sira-form";
import { SiteFooter } from "@/components/site-footer"; import { SiteFooter } from "@/components/site-footer";
import { Sayfalama, SAYFA_BOYU } from "./sayfalama"; import { Sayfalama, SAYFA_BOYU } from "./sayfalama";
@@ -17,6 +23,7 @@ import { PagePixelDivider } from "@/components/pixel-decor";
import { UniversiteKonumHaritasi } from "./universite-konum-haritasi"; import { UniversiteKonumHaritasi } from "./universite-konum-haritasi";
import { uniUlkeKonum } from "@/lib/harita-ulkeler"; import { uniUlkeKonum } from "@/lib/harita-ulkeler";
import { UniversiteProgramTablosu } from "./universite-program-tablosu"; import { UniversiteProgramTablosu } from "./universite-program-tablosu";
import { VeriKaynagi } from "./veri-kaynagi";
import { UniLogo } from "@/components/uni-logo"; import { UniLogo } from "@/components/uni-logo";
import { uniLogoYolu } from "@/lib/uni-logolar"; import { uniLogoYolu } from "@/lib/uni-logolar";
import { Skeleton } from "@/components/ui/skeleton"; import { Skeleton } from "@/components/ui/skeleton";
@@ -48,17 +55,23 @@ export function uniSayfaMetadata(slug: string, sayfa: number): Metadata {
const uni = getUniversiteBySlug(slug); const uni = getUniversiteBySlug(slug);
if (!uni) return {}; if (!uni) return {};
if (sayfa > 1) { if (sayfa > 1) {
return { return sayfaMetadata({
title: `${uni.ad} Taban Puanları 2026 (Sayfa ${sayfa})`, title: `${uni.ad} Taban Puanları 2026 (Sayfa ${sayfa})`,
description: `${uni.ad} taban puanları ve başarı sıralamaları tablosunun ${sayfa}. sayfası — toplam ${uni.programSayisi} programın 2025 yerleştirme verileri.`, description: `${uni.ad} taban puanları ve başarı sıralamaları tablosunun ${sayfa}. sayfası — toplam ${uni.programSayisi} programın 2025 yerleştirme verileri.`,
alternates: { canonical: `/universite/${slug}/sayfa/${sayfa}` }, path: `/universite/${slug}/sayfa/${sayfa}`,
}; openGraph: {
images: ustSegmentOgGorseli(
`/universite/${slug}`,
"Üniversite taban puanları ve sıralamaları — KolayTercih",
),
},
});
} }
return { return sayfaMetadata({
title: `${uni.ad} Taban Puanları ve Başarı Sıralamaları 2026`, title: `${uni.ad} Taban Puanları ve Başarı Sıralamaları 2026`,
description: `${uni.ad} 2025 yerleştirme verileri: ${uni.programSayisi} programın taban puanları, başarı sıralamaları ve kontenjanları${uni.il ? ` (${trBaslikDuzeni(uni.il)})` : ""}. 2026 tercih rehberi.`, description: `${uni.ad} 2025 yerleştirme verileri: ${uni.programSayisi} programın taban puanları, başarı sıralamaları ve kontenjanları${uni.il ? ` (${trBaslikDuzeni(uni.il)})` : ""}. 2026 tercih rehberi.`,
alternates: { canonical: `/universite/${slug}` }, path: `/universite/${slug}`,
}; });
} }
export function UniversiteIcerik({ export function UniversiteIcerik({
@@ -274,11 +287,10 @@ export function UniversiteIcerik({
<UniversiteProgramTablosu gruplar={gruplar} /> <UniversiteProgramTablosu gruplar={gruplar} />
<p className="mt-3 text-xs leading-5 text-slate-500"> <VeriKaynagi>
&quot;—&quot;: o yıl için yerleştirme verisi bulunmuyor (yeni açılan &quot;—&quot;: o yıl için yerleştirme verisi bulunmuyor (yeni açılan
veya o yıl yerleşen çıkmayan program). Kaynak: YÖK Atlas 2025 veya o yıl yerleşen çıkmayan program).{" "}
yerleştirme sonuçları. </VeriKaynagi>
</p>
<Sayfalama <Sayfalama
tabanYol={`/universite/${slug}`} tabanYol={`/universite/${slug}`}

View File

@@ -0,0 +1,29 @@
import { KATALOG_VERI_TARIHI } from "@/lib/seo";
import { cn } from "@/lib/utils";
const TARIH_METNI = new Date(
`${KATALOG_VERI_TARIHI}T00:00:00Z`,
).toLocaleDateString("tr-TR", {
day: "numeric",
month: "long",
year: "numeric",
timeZone: "UTC",
});
// Katalog tablolarının altındaki kaynak + tazelik satırı. Görünür metin olması
// bilinçli: arama/AI botları JSON-LD'ye değil sayfadaki metne güvenir.
export function VeriKaynagi({
children,
className,
}: {
children?: React.ReactNode;
className?: string;
}) {
return (
<p className={cn("mt-3 text-xs leading-5 text-slate-500", className)}>
{children}
Kaynak: YÖK Atlas 2025 yerleştirme sonuçları. Son güncelleme:{" "}
<time dateTime={KATALOG_VERI_TARIHI}>{TARIH_METNI}</time>.
</p>
);
}

View File

@@ -2,7 +2,12 @@ import type { Metadata } from "next";
import Link from "next/link"; import Link from "next/link";
import { notFound } from "next/navigation"; import { notFound } from "next/navigation";
import { getAllRehberler, getAllRehberSlugs, getRehber } from "../rehber-queries"; import { getAllRehberler, getAllRehberSlugs, getRehber } from "../rehber-queries";
import { JsonLd, articleJsonLd, breadcrumbJsonLd } from "@/lib/seo"; import {
JsonLd,
articleJsonLd,
breadcrumbJsonLd,
sayfaMetadata,
} from "@/lib/seo";
import { CtaSiraForm } from "@/features/sihirbaz/components/cta-sira-form"; import { CtaSiraForm } from "@/features/sihirbaz/components/cta-sira-form";
import { ElCizimiSema } from "./el-cizimi-sema"; import { ElCizimiSema } from "./el-cizimi-sema";
import { RehberKapak } from "./rehber-kapak"; import { RehberKapak } from "./rehber-kapak";
@@ -20,17 +25,12 @@ export function rehberStaticSlugs(): string[] {
export function rehberYaziMetadata(slug: string): Metadata { export function rehberYaziMetadata(slug: string): Metadata {
const yazi = getRehber(slug); const yazi = getRehber(slug);
if (!yazi) return {}; if (!yazi) return {};
return { return sayfaMetadata({
title: yazi.baslik, title: yazi.baslik,
description: yazi.aciklama, description: yazi.aciklama,
alternates: { canonical: `/rehber/${slug}` }, path: `/rehber/${slug}`,
openGraph: { openGraph: { type: "article", publishedTime: yazi.tarih },
type: "article", });
title: yazi.baslik,
description: yazi.aciklama,
publishedTime: yazi.tarih,
},
};
} }
export function RehberMakale({ slug }: { slug: string }) { export function RehberMakale({ slug }: { slug: string }) {

View File

@@ -1,3 +1,4 @@
import type { Metadata } from "next";
import { URUNLER } from "./credits"; import { URUNLER } from "./credits";
/** /**
@@ -14,6 +15,62 @@ export const SITE_URL =
export const SITE_NAME = "KolayTercih"; export const SITE_NAME = "KolayTercih";
/**
* Katalog verisinin (data/yokatlas.db) en son yenilendiği gün. Sitemap lastmod,
* katalog sayfalarındaki görünür "son güncelleme" satırı buradan okunur.
* Yalnızca veri GERÇEKTEN değiştiğinde (ingest/refresh sonrası) elle güncelle;
* build zamanına bağlama — her deploy'da değişen lastmod sinyali değersizleşir.
*/
export const KATALOG_VERI_TARIHI = "2026-08-09";
type SayfaMetadataGirdi = {
title: string;
description: string;
/** Kanonik yol ("/bolumler"); metadataBase ile mutlak adrese çözülür. */
path: string;
/** Varsayılan "website"; rehber yazıları article alanlarını buradan verir. */
openGraph?: NonNullable<Metadata["openGraph"]>;
};
/**
* İndekslenebilir sayfaların metadata'sı için tek kaynak. Next, segmentler
* arası metadata'yı SIĞ birleştirir: sayfa `openGraph` vermezse kök layout'unki
* (ana sayfanın başlığı + og:url'i) olduğu gibi miras kalır; verirse nesnenin
* tamamı ezilir (siteName/locale kaybolur). Bu yüzden openGraph burada eksiksiz
* üretilir. twitter:title/description/image, openGraph'tan otomatik dolar.
*/
export function sayfaMetadata({
title,
description,
path,
openGraph,
}: SayfaMetadataGirdi): Metadata {
return {
title,
description,
alternates: { canonical: path },
openGraph: {
type: "website",
locale: "tr_TR",
siteName: SITE_NAME,
url: path,
title,
description,
...openGraph,
},
};
}
/**
* Üst segmentin dosya-tabanlı OG görselini (opengraph-image.tsx) alt rotaya
* elle bağlar. Gerekçe: alt rota `openGraph` tanımlayınca sığ birleştirme üst
* segmentten gelen `images`'ı da düşürür (ör. /bolum/[slug]/sayfa/[no]).
* Boyut, src/lib/og-sayfa.tsx içindeki OG_SIZE ile aynı olmalı.
*/
export function ustSegmentOgGorseli(path: string, alt: string) {
return [{ url: `${path}/opengraph-image`, width: 1200, height: 630, alt }];
}
type JsonLdData = Record<string, unknown>; type JsonLdData = Record<string, unknown>;
export function organizationJsonLd(): JsonLdData { export function organizationJsonLd(): JsonLdData {