Mert Tosun
← Yazılar
SSR vs CSR: Dynamic Rendering ve Puppeteer ile Botlara Özel HTML

SSR vs CSR: Dynamic Rendering ve Puppeteer ile Botlara Özel HTML

Mert TosunWeb Performance

Modern web uygulamalarında en kritik karar noktalarından biri render stratejisidir: SSR mi, CSR mı, hibrit mi?
Bu karar sadece kullanıcı deneyimini değil, SEO, sunucu maliyeti, cache stratejisi ve operasyonel karmaşıklığı da etkiler.

Bu rehberde:

  1. SSR ve CSR'ın temel farklarını,
  2. Search engine botları açısından etkilerini,
  3. "Botlara SSR, kullanıcılara CSR" yaklaşımını,
  4. Dynamic rendering kavramını,
  5. Puppeteer ile botlara özel tam HTML üretimini

adım adım ele alacağız.


1) SSR vs CSR: Temel fark

SSR (Server-Side Rendering)

  • HTML sunucuda üretilir.
  • Tarayıcıya ilk gelen cevap dolu HTML olur.
  • Botlar içeriği JavaScript çalıştırmadan görebilir.

CSR (Client-Side Rendering)

  • Sunucu çoğunlukla "shell" HTML + JS bundle döner.
  • İçerik tarayıcıda JavaScript ile oluşur.
  • Botun JavaScript çalıştırma kabiliyeti kritik hale gelir.

Basit akış diyagramı:

SSR
Client/Bot --> Request --> Server(Render) --> Full HTML --> Client/Bot

CSR
Client/Bot --> Request --> Server(Shell + JS) --> Browser JS Execute --> Rendered UI

2) SEO açısından neden önemli?

Arama motoru botları yıllardır JavaScript çalıştırabiliyor, ancak:

  • Render kuyruğuna girme gecikmesi olabilir.
  • Ağır JS sayfalarda timeout veya eksik render görülebilir.
  • Kritik meta veriler (title, description, canonical, structured data) geç oluşursa indeksleme kalitesi düşebilir.

Bu yüzden özellikle içerik odaklı blog, docs, e-ticaret kategori sayfalarında ilk HTML'nin zengin olması avantaj sağlar.


3) "Botlara SSR, kullanıcıya CSR" fikri

Bu yaklaşımın özü:

  • Normal kullanıcılar hızlı, etkileşimli CSR akışını alır.
  • Botlar için önceden render edilmiş tam HTML üretilir.

Mimari görünüm:

                    +---------------------------+
Request ----------> | User-Agent Detection      |
                    +------------+--------------+
                                 |
                 +---------------+----------------+
                 |                                |
            Human User                        Search Bot
                 |                                |
             CSR App                   Render Service (Puppeteer)
        (JS bundle + APIs)             (Headless Chrome SSR)
                 |                                |
                 +---------------+----------------+
                                 |
                           HTML Response

Bu desenin adı pratikte çoğunlukla dynamic rendering olarak geçer.


4) Dynamic rendering tam olarak nedir?

Google'ın da uzun süre dokümante ettiği model:

  • UA (User-Agent) ile bot tespiti yapılır.
  • Bot için sayfanın render edilmiş snapshot'ı verilir.
  • Kullanıcı için normal SPA/CSR deneyimi korunur.

Not: Uzun vadede mümkünse gerçek SSR/SSG mimarisine geçmek daha sürdürülebilir olur. Dynamic rendering çoğunlukla geçiş dönemlerinde güçlü bir çözümdür.


5) Puppeteer ile botlara özel SSR cevabı

5.1 Akış

Bot Request
   |
   v
Node Middleware (isBot?)
   |
   +-- no  --> normal CSR response
   |
   +-- yes --> Puppeteer open page
               wait until network idle
               read final HTML
               cache result
               return HTML

5.2 Örnek bot tespiti

const BOT_UA =
  /googlebot|bingbot|yandex|duckduckbot|baiduspider|slurp|twitterbot|facebookexternalhit|linkedinbot/i;

function isBot(userAgent: string): boolean {
  return BOT_UA.test(userAgent);
}

5.3 Basit Express + Puppeteer örneği

import express from 'express';
import puppeteer from 'puppeteer';

const app = express();
const ORIGIN = 'https://example.com';
const cache = new Map<string, { html: string; expiresAt: number }>();

function isBot(ua = '') {
  return /googlebot|bingbot|yandex|duckduckbot|twitterbot|facebookexternalhit/i.test(ua);
}

app.get('*', async (req, res, next) => {
  try {
    const ua = req.headers['user-agent'] || '';
    if (!isBot(String(ua))) return next(); // human -> normal app

    const key = req.originalUrl;
    const hit = cache.get(key);
    const now = Date.now();
    if (hit && hit.expiresAt > now) {
      res.setHeader('x-render-cache', 'HIT');
      return res.status(200).send(hit.html);
    }

    const browser = await puppeteer.launch({ headless: true });
    const page = await browser.newPage();
    await page.setUserAgent(String(ua));
    await page.goto(`${ORIGIN}${req.originalUrl}`, { waitUntil: 'networkidle2', timeout: 30000 });
    const html = await page.content();
    await browser.close();

    cache.set(key, { html, expiresAt: now + 1000 * 60 * 5 }); // 5 dk cache
    res.setHeader('x-render-cache', 'MISS');
    return res.status(200).send(html);
  } catch (err) {
    return next(err);
  }
});

6) Operasyonel kritik noktalar

  • Timeout yönetimi: Render servisinde üst sınır koyun (örn. 10-30s).
  • Cache şart: Her bot isteğinde Chrome açmak pahalıdır.
  • Resource engelleme: Gerekirse analytics/script gibi gereksiz kaynakları bloklayın.
  • Ayrık servis: Yoğun trafikte render'ı ayrı bir servis/queue üstüne taşıyın.
  • Gözlemlenebilirlik: Bot render süresi, hata oranı, cache hit oranı metriklerini toplayın.

7) Dynamic rendering ne zaman mantıklı?

Uygun senaryo:

  • Mevcut SPA'nız var ve hızlıca SEO iyileştirmesi istiyorsunuz.
  • Kısa/orta vadede Next.js SSR/SSG gibi tam dönüşüm mümkün değil.

Uygun olmayan senaryo:

  • Yeni projeye sıfırdan başlıyorsunuz (doğrudan SSR/SSG/hybrid kurmak daha temiz).

Sonuç

SSR ve CSR rakip değil; ürün ihtiyacına göre farklı güçlü yönleri olan iki modeldir.
Dynamic rendering, özellikle SPA projelerinde bot görünürlüğünü artırmak için pragmatik bir köprü sunar. Puppeteer ile botlara özel tam HTML üretip kullanıcıya CSR deneyimini koruyabilirsiniz.

İdeal hedef ise bu geçişi kalıcı mimariye evirmek: kritik sayfaları SSR/SSG'ye taşıyıp dynamic rendering yükünü zamanla azaltmak.