Render farkını test etmek: kaynak kod ve DOM karşılaştırması

Bir sayfanın iki hâli var: sunucunun gönderdiği ham HTML ve JavaScript çalıştıktan sonra tarayıcıda oluşan DOM. İki hâl aynıysa JavaScript SEO'su hakkında endişelenecek pek bir şey yok. Farklıysa, hangi alanın farklı olduğunu bilmek gerekiyor: title mı değişiyor, canonical mı ekleniyor, içerik mi sonradan geliyor?

Bu farkı gözle görmek, JavaScript ağırlıklı bir sitede yaptığım ilk kontrollerden biri. Aşağıda en basitinden otomatiğine dört yöntemi ve karşılaştırmayı raporlayan küçük bir Node.js betiğini anlatıyorum.

Neden önemli?

Google ham HTML'i taramada, render edilmiş DOM'u da render aşamasında görüyor. İkisi arasında çelişki olduğunda sorun çıkıyor. Örneğin ham HTML'de noindex varsa Google render etmeden sayfayı dışarıda bırakabilir; JavaScript'in bu etiketi sonradan kaldırması işe yaramayabilir. Canonical ham HTML'de bir adresi, render sonrasında başka bir adresi gösteriyorsa Google'a çelişkili sinyal gidiyor.

Ayrıca Google dışındaki birçok arama motoru ve yapay zekâ tarayıcısı JavaScript çalıştırmayabiliyor. Onlar için sayfanız sadece ham HTML'den ibaret. Sürecin Google tarafını Googlebot JavaScript'i nasıl işliyor yazısında anlattım.

Yöntem 1: curl ile ham HTML

En hızlı kontrol, sayfayı JavaScript çalıştırmayan bir araçla indirmek:

# Ham HTML'i dosyaya kaydet (yönlendirmeleri takip ederek)
curl -sL -A "Mozilla/5.0" https://example.com/sayfa -o ham.html

# Önemli etiketlere hızlıca bak
grep -ioE '<title>[^<]*</title>|<link[^>]*rel="canonical"[^>]*>|<meta[^>]*name="robots"[^>]*>' ham.html

Bu yöntem regex ile çalıştığı için kaba bir kontrol; özellik sırası farklıysa ya da tırnak işaretleri değişikse kaçırabilir. Ama "title var mı, içerik var mı" sorusuna saniyeler içinde cevap veriyor. ham.html dosyasını açıp ana metnin bir cümlesini aramak da iyi bir test: bulamıyorsanız içerik JavaScript ile geliyor demektir.

Yöntem 2: Chrome DevTools

Chrome'da iki farklı görünüm var ve karıştırılmaları çok yaygın:

  • Sayfa kaynağını görüntüle (Ctrl+U): Sunucunun gönderdiği ham HTML. JavaScript'ten etkilenmez.
  • DevTools > Elements paneli: O anki canlı DOM, yani render edilmiş hâl.

İkisini yan yana açıp <head> bölümünü karşılaştırmak iyi bir başlangıç. Bir başka yöntem de JavaScript'i kapatmak: DevTools açıkken Ctrl+Shift+P ile komut menüsünü açıp "Disable JavaScript" yazın ve sayfayı yenileyin. Karşınıza çıkan sayfa, JavaScript çalıştırmayan bir tarayıcının gördüğüne yakın.

Yöntem 3: URL Denetimi

Google'ın gördüğü render edilmiş HTML için en güvenilir kaynak Search Console'daki URL Denetimi (URL Inspection) aracı. "Canlı URL'yi test et" (Test live URL) ile sayfayı test edip "Test edilen sayfayı görüntüle" (View tested page) panelindeki HTML sekmesinden render edilmiş kodu kopyalayabilirsiniz. Aynı panelde ekran görüntüsü ve yüklenemeyen kaynaklar da görünüyor.

Bu aracın sınırı, sadece doğrulanmış mülklerde ve tek tek URL'lerle çalışması. Çok sayıda sayfayı karşılaştırmak için otomatik bir yönteme ihtiyaç var.

Elinizde iki HTML olduğunda, ham ve render edilmiş sürümü ayrı ayrı kaynak kod analizörüne yapıştırıp title, meta etiketleri, başlıklar ve linkleri iki sürüm için yan yana okuyabilirsiniz. Tek sayfalık kontroller için bu çoğu zaman yeterli.

Yöntem 4: Puppeteer ile otomatik karşılaştırma

Aşağıdaki betik bir URL alıyor, ham HTML'i fetch ile, render edilmiş DOM'u Puppeteer ile alıyor ve iki dosyaya kaydediyor. Sonra iki HTML'i aynı şekilde ayrıştırıp title, meta description, canonical, meta robots, ilk H1, link sayısı ve kelime sayısını tablo olarak karşılaştırıyor.

Ayrıştırma için ek bir kütüphane kullanmadım: iki HTML'i de boş bir sekmede tarayıcının kendi DOMParser'ı ile işliyorum. DOMParser script çalıştırmadığı için ham HTML, JavaScript'ten etkilenmeden olduğu gibi ayrıştırılıyor. Böylece iki sürüm tamamen aynı kurallarla ölçülüyor.

Gereksinimler: Node.js 18 veya üzeri (yerleşik fetch için) ve npm install puppeteer.

render-farki.mjs
// Kullanım: node render-farki.mjs https://example.com/sayfa
import puppeteer from 'puppeteer';
import { writeFile } from 'node:fs/promises';

const url = process.argv[2];
if (!url) {
  console.error('Kullanım: node render-farki.mjs <url>');
  process.exit(1);
}
const UA = 'Mozilla/5.0 (compatible; RenderFarkiTesti/1.0)';

// 1) Ham HTML: JavaScript çalışmadan sunucunun döndürdüğü
const res = await fetch(url, { headers: { 'User-Agent': UA } });
const raw = await res.text();

// 2) Render edilmiş DOM
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.setUserAgent(UA);
await page.goto(url, { waitUntil: 'networkidle0', timeout: 30000 });
const rendered = await page.content();

await writeFile('ham.html', raw);
await writeFile('render.html', rendered);

// 3) Her iki HTML'i boş bir sekmede DOMParser ile aynı şekilde ölç
// (boş sekme, sitenin CSP/Trusted Types kurallarından etkilenmez)
const bos = await browser.newPage();
const olc = (html) => bos.evaluate((src) => {
  const d = new DOMParser().parseFromString(src, 'text/html');
  const ozellik = (sec, ad) => d.querySelector(sec)?.getAttribute(ad)?.trim() ?? '';
  const govde = d.body ? d.body.cloneNode(true) : null;
  govde?.querySelectorAll('script, style, noscript, template').forEach((n) => n.remove());
  return {
    title: d.title.trim(),
    description: ozellik('meta[name="description" i]', 'content'),
    canonical: ozellik('link[rel~="canonical" i]', 'href'),
    robots: ozellik('meta[name="robots" i]', 'content'),
    h1: d.querySelector('h1')?.textContent.replace(/\s+/g, ' ').trim() ?? '',
    linkler: d.querySelectorAll('a[href]').length,
    kelimeler: (govde?.textContent ?? '').split(/\s+/).filter(Boolean).length,
  };
}, html);

const a = await olc(raw);
const b = await olc(rendered);
await browser.close();

console.log(`HTTP durum kodu: ${res.status}`);
console.table(Object.keys(a).map((alan) => ({
  alan,
  ham: String(a[alan]).slice(0, 50),
  render: String(b[alan]).slice(0, 50),
  durum: a[alan] === b[alan] ? 'aynı' : 'FARKLI',
})));

Çalıştırdığınızda konsolda her alan için ham ve render değerlerini, en sağda da aynı ya da FARKLI etiketini görüyorsunuz. ham.html ve render.html dosyaları da çalışma dizininde kalıyor; ayrıntılı inceleme ya da diff için kullanabilirsiniz.

Betiği kullanırken dikkat ettiklerim

  • networkidle0, ağ trafiği tamamen durana kadar bekliyor. Sürekli bağlantı açık tutan (analitik ping'leri, websocket) sitelerde zaman aşımına düşebilir; o durumda networkidle2 kullanın.
  • Kelime sayısında küçük farklar normal; çerez bildirimi ya da sonradan eklenen widget'lar birkaç kelime ekleyebiliyor. Benim baktığım, büyük farklar: ham HTML'de 40 kelime, render'da 1.200 kelime gibi bir tablo, ana içeriğin JavaScript ile geldiğini gösteriyor.
  • Link sayısındaki büyük fark, menü ya da ürün listesi linklerinin sadece render sonrası var olduğu anlamına geliyor. Google bunları render sonrası bulabiliyor ama keşif gecikiyor.
  • User agent'ı Googlebot olarak değiştirmek mümkün, ama bu gerçek Googlebot testi değil. Bazı siteler bot user agent'larına farklı yanıt veriyor ya da engelliyor. Google'ın gördüğü için son söz her zaman URL Denetimi'nin.
  • Betik tek URL için. Birden fazla sayfayı test etmek için URL listesini bir döngüde aynı tarayıcı örneğiyle işleyebilirsiniz; her URL için yeni tarayıcı açmak gereksiz yavaşlık.

Farkları nasıl yorumlamalı?

AlanFark varsa
title, descriptionKabul edilebilir ama riskli; ham HTML'de doğru değer olmalı
canonicalÇelişkili sinyal; iki sürümde aynı değer olmalı
robotsHam HTML'de noindex, render'da yoksa: sayfa dizine girmeyebilir
h1, kelime sayısıAna içerik JavaScript'e bağlı; render stratejisini gözden geçirin
link sayısıKeşif render'a bağlı; önemli linkleri ham HTML'e taşıyın

Farkın kalıcı çözümü genelde render stratejisinde. SSR, SSG ya da ISR ile ana içeriği ve meta etiketleri ilk HTML'e taşımak, bu tablodaki farkların çoğunu ortadan kaldırıyor. Seçenekleri React/Vue sitelerinde SSR, SSG ve prerender yazısında karşılaştırdım.

Özetle

Hızlı kontrol için curl ve Ctrl+U, Google'ın gerçekten ne gördüğü için URL Denetimi, çok sayıda sayfa ve tekrarlanabilir sonuç için Puppeteer betiği. Hangi yöntemi kullanırsanız kullanın hedef aynı: title, canonical, robots, ana içerik ve önemli linkler ham HTML'de olmalı ve render sonrasında değişmemeli. İki sürüm aynıysa, JavaScript sitenizin SEO'sunu riske atmıyor demektir.

Comments / questions

If you have a question about any topic or wish to contact me: Telegram or send an email to [email protected] Thanks!

Related pages