Sayfa tarayıcıda görünüyor ama kaynak kodunda yok: JavaScript render stratejisi ve arama görünürlüğü
Sayfanız tarayıcıda sorunsuz açılıyor ama "sayfa kaynağını görüntüle" dediğinizde içerik yoksa, o içerik sunucudan gelmiyor demektir. Bu durumda Google çoğu zaman sayfayı yine de render edip indeksler, çünkü JavaScript çalıştıran bir tarayıcı motoru kullanır. ChatGPT ve Claude'un tarayıcı botları ise JavaScript çalıştırmaz; onlar için sayfanız boş bir kabuktur. Karar "SPA yazmayalım" değil, hangi sayfaların içeriğinin ilk HTML yanıtında bulunması gerektiğidir. Cevabı sayfa tipine göre verilir, siteye göre değil.
Önce teşhis: sayfa gerçekten boş mu geliyor?
Tarayıcı DevTools'taki Elements sekmesi size render edilmiş DOM'u gösterir, yani JavaScript çalıştıktan sonraki hâli. Botların çoğu o hâli görmez. Doğru test ham yanıta bakmaktır:
curl -sL https://siteniz.com/urunler/abc | grep -i "<h1"
Ürün adı, fiyat, açıklama ve <title> bu çıktıda yoksa, sunucu onları göndermiyordur. İkinci test tarayıcıda JavaScript'i kapatıp sayfayı açmak. Üçüncüsü Search Console'daki URL İnceleme aracının canlı testi: "Taranan sayfayı görüntüle" bölümünde Google'ın gördüğü HTML ve render sırasında yüklenemeyen kaynaklar listelenir.
Search Console'da "Tarandı, şu anda dizine eklenmedi" sayısının yüksek olması tek başına render sorununu kanıtlamaz, ama JavaScript ağırlıklı bir sitede ilk bakılacak yerdir.
Google render ediyor, ama ücretsiz değil
Google kendi dokümanında süreci üç aşamaya ayırıyor: tarama, render ve indeksleme. Sayfa 200 yanıtı döndürdükten sonra render kuyruğuna alınır, headless Chromium JavaScript'i çalıştırır, ardından ortaya çıkan HTML yeniden ayrıştırılır. Google aynı sayfada hangi kuyrukta beklendiğinin dışarıdan belli olmadığını da söylüyor.
Bu kuyruğun üç pratik sonucu var. Birincisi gecikme: sunucu tarafında üretilen bir sayfa ilk taramada indekslenebilirken, JavaScript ile üretilen içerik ikinci bir işlemi bekler. İkincisi kırılganlık: render sırasında bir API çağrısı zaman aşımına uğrarsa veya bir dosya yüklenemezse, Google eksik sayfayı indeksler. Üçüncüsü robots.txt: Google'ın JavaScript SEO temelleri rehberi bunu açıkça yazıyor, engellenmiş dosyalardaki veya engellenmiş sayfalardaki JavaScript render edilmez. /static/ ya da /api/ yolunu robots.txt'te kapatan bir satır, sayfanın tamamını boşaltabilir.
Aynı doküman bir ayrıntıya daha dikkat çekiyor: Google bir noindex etiketi gördüğünde render'ı ve JavaScript çalıştırmayı atlayabilir. Yani ilk HTML'de noindex bulunup sonradan JavaScript ile kaldırılan sayfalar indekslenmez.
Yapay zeka tarafında durum farklı
Burada varsayımlar işe yaramıyor. Vercel'in 17 Aralık 2024'te yayımladığı AI crawler analizi, kendi ağındaki ve nextjs.org üzerindeki bot trafiğini inceledi. Sonuç net: büyük yapay zeka tarayıcılarının hiçbiri JavaScript render etmiyor. ChatGPT ve Claude botları JavaScript dosyalarını indiriyor (isteklerinin sırasıyla %11,50 ve %23,84'ü), ama çalıştırmıyor. Ölçek de küçük değil: o bir aylık pencerede GPTBot 569 milyon, Claude 370 milyon, PerplexityBot 24,4 milyon istek yapmış. Googlebot'un 4,5 milyarlık hacminin yanında az görünüyor, yine de yok sayılacak bir trafik değil.
Gemini bu listenin dışında, çünkü Googlebot'un altyapısını kullanıyor ve render edebiliyor.
Pratik karşılığı şu: Google'da sıralaması iyi olan bir sayfa, ChatGPT'ye sorulduğunda hiç anılmayabilir. İçeriğiniz ilk HTML'de yoksa, dil modeli onu okumaz. Yapay zeka aramada görünürlük tarafında yaptığınız her çalışmanın önkoşulu budur.
Paylaşım önizlemeleri de JavaScript beklemiyor
WhatsApp'ta, Slack'te veya LinkedIn'de bir bağlantı paylaştığınızda önizleme kartını üreten botlar sayfanın ham HTML'ini okur. Open Graph etiketlerini React Helmet benzeri bir kütüphaneyle çalışma zamanında ekliyorsanız, kart ya boş çıkar ya da tüm sayfalarda aynı genel başlığı gösterir. Başlık, açıklama ve görsel etiketleri sunucunun gönderdiği HTML'de olmalı. Bunu test etmek de kolay: curl çıktısında og:title arayın.
Dört seçenek ve gerçek maliyetleri
İstemci tarafı render (CSR). Sunucu boş bir kabuk gönderir, içerik tarayıcıda oluşur. Sunucu maliyeti en düşük seçenek, dağıtımı basit. İlk HTML'de içerik yok.
Statik üretim (SSG). Sayfalar derleme sırasında HTML'e dönüşür, CDN'den servis edilir. En hızlı ve en ucuz yol. Sınırı şu: içerik değiştiğinde yeniden üretim gerekir, on binlerce sayfalı kataloglarda derleme süresi sorun olur.
Sunucu tarafı render (SSR). HTML her istekte sunucuda üretilir. İçerik her zaman güncel, ilk yanıtta tam. Karşılığında çalışan bir sunucu, veritabanına yük ve dikkatli bir önbellek stratejisi gerekir. Önbelleksiz SSR, TTFB'yi yükselten en yaygın sebeplerden biridir.
Artımlı yeniden üretim (ISR benzeri yaklaşımlar). Sayfa statik üretilir, belirli aralıklarla veya bir tetikleyiciyle arka planda tazelenir. Katalog ve blog gibi "sık okunan, seyrek değişen" içerikte en iyi dengeyi genelde bu kurar.
Bu dördü birbirinin alternatifi değil. Modern framework'lerde aynı uygulamada sayfa bazında seçilirler ve doğru kurulum genelde karışıktır.
Hangi sayfa hangisini hak ediyor?
Tek bir ayrım işin çoğunu çözüyor: sayfa herkese açık ve bulunmak istiyorsa, içeriği ilk HTML'de olmalı. Girişin arkasındaysa, istemci tarafında render edilmesinde sakınca yok.
- Pazarlama ve hizmet sayfaları, blog, dokümantasyon: statik üretim. Değişim sıklığı düşük, arama değeri yüksek.
- Ürün ve kategori sayfaları: statik üretim veya artımlı tazeleme. Fiyat ve stok gibi alanlar istemcide güncellenebilir, ama ürün adı ve açıklaması HTML'de dursun.
- Site içi arama sonuçları ve filtreli listeler: istemcide render edin ve indekslemeyin. Bu sayfalar kullanıcı için değerli, arama motoru için genelde gürültü.
- Panel, hesap ekranları, yönetim arayüzü: CSR tamamen yeterli. Burada SEO diye bir mesele yok, kimse bu sayfaları aramıyor.
Bu ayrımı yapmadan "her şeyi SSR'a geçirelim" demek, sunucu faturasını ve karmaşıklığı hiçbir karşılığı olmadan büyütür.
Bot'a ayrı HTML sunmak (dynamic rendering) çözüm değil
Eski bir yöntem hâlâ dolaşımda: bot user-agent'ını yakalayıp ona prerender edilmiş HTML, kullanıcıya SPA sunmak. Google bu sayfanın dokümanını büyük ölçüde boşalttı ve geriye kalan ifade net: "Dynamic rendering bir geçici çözümdü, uzun vadeli bir çözüm değil." Önerilen alternatifler sunucu tarafı render, statik render ve hydration olarak sıralanıyor.
Sebepleri pratik. İki ayrı çıktı iki ayrı bakım yükü demek ve ikisi zamanla birbirinden ayrışır. Bot'a gösterdiğiniz HTML kullanıcıya gösterilenden farklılaşırsa cloaking sınırına yaklaşırsınız. Üstüne, prerender servisinin kendisi yeni bir arıza noktasıdır: o servis düşerse botlar hata sayfası görür ve bunu haftalarca fark etmezsiniz.
Geçiş döneminde kısa süreli bir köprü olarak kullanılabilir. Kalıcı mimari olarak seçilmemeli.
SPA'lerde tekrar eden dört hata
Adres çubuğunda fragment kullanmak. site.com/#/urunler/abc biçimindeki adresler güvenilir şekilde taranmaz. Google doğrudan History API kullanmayı öneriyor, yani her içerik kendi gerçek URL'sinde yaşamalı.
Bağlantıları <a href> ile yazmamak. onClick ile yönlendiren div veya button elemanları tarayıcı için bağlantı değildir. Google bağlantıları href taşıyan <a> elemanlarında bulur. Router kullanıyor olmanız bu kuralı değiştirmez, çıktıda gerçek bir href olmalı.
Yumuşak 404. İstemci tarafı yönlendirme HTTP durum kodu döndüremez, bu yüzden olmayan ürün sayfası 200 ile "Bulunamadı" yazısı gösterir. Google'ın önerisi ya 404 döndüren bir sunucu URL'sine yönlendirmek ya da hata ekranına noindex eklemek. Aksi hâlde yüzlerce boş sayfa indekslenir.
İçeriği etkileşimin arkasına koymak. Sekme, akordeon veya sonsuz kaydırma ile yüklenen metin, render edilmiş HTML'de yoksa indekslenmez. Google'ın kuralı tek cümle: içerik render edilmiş HTML'de görünmüyorsa indekslenemez.
SSR'ın faturası: hız ve dayanıklılık
Sunucuda render etmeye geçmek performans sorunlarını otomatik olarak çözmez, sadece yerini değiştirir. Üç kalemi baştan planlayın.
TTFB. Her istekte veritabanına giden bir SSR sayfası, statik bir dosyadan yavaştır. CDN önbelleği ve sayfa bazında önbellek ömrü olmadan ölçeklenmez.
Hydration maliyeti. HTML'i sunucuda üretip aynı bileşen ağacını tarayıcıda yeniden canlandırmak ana iş parçacığını meşgul eder. Bu doğrudan INP'yi etkiler, yani kullanıcı tıkladığında ekranın ne kadar geç yanıt verdiğini. Core Web Vitals ve gerçek kullanıcı verisi tarafında ölçtüğünüz metrik budur. Sunucuya taşıdığınız her bileşen tarayıcıya inen JavaScript'i azaltır, o yüzden hangi bileşenin gerçekten etkileşime ihtiyacı olduğunu tek tek sorun.
Dayanıklılık. CSR'da bir API yavaşlarsa sadece o bölüm geç gelir. SSR'da aynı API'nin yavaşlaması sayfanın tamamını bekletir. Kritik olmayan veriyi akışa (streaming) alın veya istemciye bırakın. Yük testi ve kapasite planlaması bu kararın sağlamasını yapmanın en hızlı yolu.
Mevcut SPA'den çıkış planı
Yeniden yazmaya gerek yok. Rota rota ilerleyin.
Birinci adım, listeyi veriye dayandırmak: hangi sayfalar organik trafik getiriyor, hangileri getirmesi gerekirken getirmiyor. Sunucu loglarında bot user-agent'larını (Googlebot, GPTBot, ClaudeBot, PerplexityBot) ayırıp hangi yolları çektiklerine bakın. Bu liste genelde beş on şablona iner.
İkinci adım, en değerli şablonu seçip onu statik üretime veya SSR'a taşımak. Tek bir ürün sayfası şablonunu dönüştürmek, binlerce URL'yi aynı anda düzeltir.
Üçüncü adım, her geçişten sonra doğrulamak. curl çıktısında içerik var mı, URL İnceleme aracı aynı şeyi gösteriyor mu, yapısal veri hâlâ geçerli mi. Adres yapısı da değişiyorsa yenileme ve taşımada SEO yazısındaki yönlendirme planını atlamayın.
Dördüncü adım, geri kalanı olduğu gibi bırakmak. Panel ve hesap ekranlarını dönüştürmenin kimseye faydası yok.
Bunu bir testle kalıcı hâle getirin
Render regresyonları sessizdir. Bir kütüphane yükseltmesi, bir bileşenin istemciye kayması veya yanlış konmuş bir useEffect sayfayı yeniden boşaltır ve bunu haftalar sonra trafik düştüğünde fark edersiniz.
Çözüm basit: dağıtım hattına birkaç satırlık bir kontrol ekleyin. Her yayından sonra üç beş kritik URL'i sunucudan çekin ve HTML'de <h1>, <title>, og:title ve sayfaya özgü bir metin parçası var mı diye bakın. Yoksa yayını kırın. Aynı kontrolü test ortamı ile canlı ortam farkını yakalamak için her iki ortamda da koşturun, çünkü prerender ve önbellek davranışı ortamlar arasında sessizce farklılaşır.
Uzun vadede teknoloji seçimi de bu kararı belirliyor. Yeni bir proje başlatıyorsanız, render stratejisini sonradan eklenecek bir eklenti olarak değil, teknoloji seçiminin parçası olarak değerlendirin.
Bu hafta yapılabilecek üç şey
Bir: en çok trafik getiren beş sayfanızı curl ile çekin ve içeriğin HTML'de olup olmadığını görün. Beş dakika sürer, kararın yarısını verir.
İki: robots.txt dosyanızda JavaScript, CSS veya veri uçlarını engelleyen bir satır var mı kontrol edin. Varsa, Google'ın o sayfaları hiç render edemiyor olma ihtimali yüksek.
Üç: sunucu loglarından son 30 günde GPTBot ve ClaudeBot'un hangi URL'leri çektiğini çıkarın. O URL'lerin ham HTML'i boşsa, yapay zeka tarafında görünmüyorsunuz demektir.
Wedevit olarak mevcut uygulamanızın render davranışını sayfa tipi bazında çıkarıyor, hangi şablonların sunucuya taşınması gerektiğini önceliklendiriyor, geçişi rota rota planlıyor ve dağıtım hattına render regresyonunu yakalayan kontrolleri ekliyoruz. Somut ilk adım: en çok ciro getiren ürün sayfanızın ham HTML'inde fiyat yazıyor mu, bugün bakın.
Bu konuda yardıma mı ihtiyacınız var?