Speculation Rules API ile Anında Sayfa Geçişleri

Core Web Vitals rehberinde her metriği tek tek nasıl iyileştireceğinizi ele almıştık. Peki tek bir teknikle LCP, INP ve CLS’yi aynı anda iyileştirebilseydiniz?

Speculation Rules API tam olarak bunu yapar. Tarayıcıya kullanıcının muhtemelen gideceği sayfayı önceden getirmesini, hatta tamamen render etmesini söylersiniz. Kullanıcı bağlantıya tıkladığında sayfa yüklenmez — zaten yüklenmiştir. LCP neredeyse sıfıra iner, yükleme sırasındaki düzen kaymaları kullanıcı sayfayı görmeden önce gerçekleşir ve JavaScript tıklamadan önce çalışmayı bitirdiği için INP iyileşir.

Bu, 2024–2026 döneminin en az konuşulan ama en yüksek etkili performans kazancıdır ve JavaScript paket boyutunuza tek bayt eklemez.

Spekülatif Yükleme Nedir?

Fikir yeni değil. <link rel="prefetch"> yıllardır var. Yeni olan, tarayıcıya hangi sayfaları ve ne zaman spekülatif olarak yükleyeceğini anlatmanızı sağlayan ifade gücü yüksek bir sözdizimidir.

Speculation Rules API iki farklı seviye sunar:

SeviyeNe Yapar?MaliyetKazanç
prefetchYalnızca HTML yanıt gövdesini indirir, alt kaynakları indirmezDüşük — tek bir GET isteğiBelirgin derecede hızlı
prerenderSayfayı görünmez bir sekmede tamamen yükler ve render eder, JS çalışırYüksek — bir <iframe> kadarNeredeyse anında

Aradaki fark kritik. Prefetch’i geniş çapta uygulayın — risk düşük, kazanç gerçek. Prerender’ı seçici kullanın — sadece kullanıcının o sayfaya gitme olasılığı yüksekse. Kullanıcının gitmediği her prerender, boşa harcanmış bellek ve bant genişliğidir.

Not: Prerender edilen URL’ler zaten prefetch de edilir. İkisini aynı URL için birlikte tanımlamanıza gerek yok.

Temel Kullanım: URL Listeleri

En basit biçim, sayfaya bir <script type="speculationrules"> elementi eklemektir. İçeriği JSON formatında, çalıştırılabilir JavaScript değil.

<script type="speculationrules">
  {
    "prerender": [{ "urls": ["/sonraki-yazi", "/hakkimda"] }]
  }
</script>

Bu yaklaşım, bir sonraki adımın belirgin olduğu durumlarda iyidir: çok adımlı bir form akışı, sayfalanmış bir arşivin “sonraki sayfa” bağlantısı veya bir haber sitesinin en yeni makalesi.

Ancak bir blog ana sayfasında onlarca bağlantı varsa, hepsini listelemek hem pratik değildir hem de işin mantığına ters düşecektir, bunun için doküman kuralları var.

Doküman Kuralları: where Sözdizimi

Doküman kuralları, sayfadaki bağlantıları URL desenine veya CSS seçicisine göre eşleştirir. Böylece statik bir liste tutmak yerine tarayıcıya bir politika anlatırsınız.

<script type="speculationrules">
  {
    "prerender": [
      {
        "where": {
          "and": [
            { "href_matches": "/*" },
            { "not": { "href_matches": "/cikis" } },
            { "not": { "href_matches": "/*\?*(^|&)sepete-ekle=*" } },
            { "not": { "selector_matches": ".prerender-etme" } },
            { "not": { "selector_matches": "[rel~=nofollow]" } }
          ]
        },
        "eagerness": "moderate"
      }
    ]
  }
</script>

Buradaki her satır önemli:

  • href_matches: "/*" — yalnızca aynı origin’deki bağlantılar. URL Pattern API sözdizimini kullanır.
  • Çıkış ve “sepete ekle” URL’leri hariç tutulur. Bu bir detay değil, zorunluluktur, nedenini birazdan göreceğiz.
  • .prerender-etme sınıfı, tek tek bağlantılara elle kaçış kapısı sağlar.
  • [rel~=nofollow] genellikle güvenilmeyen veya kullanıcı tarafından gönderilmiş bağlantıları işaretler.

and, not ve href_matches Dışındakiler

where bloğu iç içe geçebilir. or da desteklenir. Örneğin yalnızca blog yazılarını prerender etmek isterseniz:

<script type="speculationrules">
  {
    "prerender": [
      {
        "where": {
          "or": [{ "href_matches": "/posts/*" }, { "href_matches": "/projeler/*" }]
        },
        "eagerness": "moderate"
      }
    ]
  }
</script>

Eagerness: Spekülasyon Ne Zaman Tetiklenir?

eagerness ayarı, kazanç ile kaynak israfı arasındaki dengeyi kurduğunuz yerdir. Dört değer vardır:

DeğerNe Zaman Tetiklenir?Kullanım Yeri
immediateKurallar okunur okunmazKısa URL listeleri, çok emin olduğunuz durumlar
eagerMasaüstünde 10ms hover; mobilde bağlantı görünüm alanına girdikten 50ms sonraHafif, statik siteler
moderateMasaüstünde 200ms hover veya pointerdown; mobilde görünüm alanı sezgiselleriÇoğu site için en iyi başlangıç
conservativepointerdown veya dokunma anındaAğır sayfalar, sınırlı kaynak

Varsayılanlar dikkat ister: liste kuralları için varsayılan immediate, doküman kuralları için varsayılan conservative‘dir. Yani doküman kuralı yazıp eagerness belirtmezseniz, spekülasyon ancak kullanıcı tıklamaya başladığında tetiklenir ve kazancınızın büyük kısmını kaybedersiniz.

Çoğu blog ve içerik sitesi için doğru başlangıç noktası şudur:

<script type="speculationrules">
  {
    "prerender": [{ "where": { "href_matches": "/*" }, "eagerness": "moderate" }]
  }
</script>

200 milisaniye hover, kullanıcının niyetini anlamak için şaşırtıcı derecede güçlü bir sinyaldir ve tıklamaya kadar genellikle sayfayı hazırlamaya yetecek zamanı bırakır.

Chrome’un Limitleri

Chrome, aşırı kullanımı engellemek için sınır koyar:

EagernessPrefetchPrerender
immediate5010
eager / moderate / conservative2 (FIFO)2 (FIFO)

Etkileşime bağlı ayarlar FIFO mantığıyla çalışır: limit dolduğunda en eski spekülasyon iptal edilir. İptal edilen bir spekülasyon tamamen boşa gitmez, önbelleğe alınabilir kaynaklar HTTP önbelleğinde kalır.

Chrome ayrıca şu durumlarda spekülasyonu hiç çalıştırmaz:

  • Save-Data modu etkinse
  • Pil tasarrufu modunda ve düşük pil seviyesinde
  • Bellek kısıtlı cihazlarda
  • “Sayfaları önceden yükle” ayarı kapalıysa
  • Arka plan sekmelerinde açılan sayfalarda

Bu, API’yi tasarımı gereği kullanıcının tercihine saygılı yapar. fetch() ile elle yazacağınız bir prefetch çözümü bunların hiçbirini yapmaz.

HTTP Header ile Dağıtım

Kuralları HTML’e gömmek yerine bir HTTP başlığıyla da sunabilirsiniz. Bu, CDN seviyesinde dağıtım için veya kuralları tüm sayfalarda tek yerden yönetmek için kullanışlıdır.

Speculation-Rules: "/speculationrules.json"

JSON dosyası doğru MIME tipiyle sunulmalıdır:

Content-Type: application/speculationrules+json

Göreli URL’ler kullanacaksanız "relative_to": "document" anahtarını ekleyin. Aksi halde göreli URL’ler JSON dosyasının konumuna göre çözümlenir, dokümana göre değil.

Inline script ve HTTP başlığını aynı anda kullanabilirsiniz; tüm kurallar birleştirilir.

Vercel’de Yapılandırma

Bu blog Vercel’de çalışıyor. vercel.json üzerinden başlığı şöyle ekleyebilirsiniz:

{
  "headers": [
    {
      "source": "/(.*)",
      "headers": [{ "key": "Speculation-Rules", "value": ""/speculationrules.json"" }]
    },
    {
      "source": "/speculationrules.json",
      "headers": [{ "key": "Content-Type", "value": "application/speculationrules+json" }]
    }
  ]
}

Kritik Bölüm: Ne Zaman Güvenli Değil?

Burası çoğu rehberin geçiştirdiği ve üretimde sizi yakan kısımdır. Spekülatif yükleme, sunucunuza gerçek bir GET isteği gönderir. O istek bir yan etki üretiyorsa, kullanıcı hiç tıklamasa bile yan etki gerçekleşir.

Şu URL’ler prefetch için güvenli değildir:

  • Oturum kapatma URL’leri — kullanıcı farkında olmadan çıkış yapar
  • Dil değiştirme URL’leri
  • “Sepete ekle” URL’leri
  • Tek kullanımlık şifre (OTP) gönderen giriş akışı URL’leri
  • Kullanım kotası tüketen URL’ler (aylık ücretsiz makale hakkı gibi)
  • Sunucu tarafı reklam dönüşümü tetikleyen URL’ler

Prerender’da risk daha da büyüktür, çünkü sayfanın JavaScript’i de çalışır. Buna ek olarak şunlara dikkat edin:

  • Yüklenirken localStorage veya IndexedDB’yi değiştiren sayfalar — kullanıcının o an baktığı başka bir sekmeyi bozabilir
  • Analitik olayı gönderen veya reklam gösterimi kaydeden sayfalar — raporlarınız şişer

Pratik kural: Bir URL robots.txt dosyanızda engelliyse veya yalnızca kimliği doğrulanmış kullanıcılar erişebiliyorsa, spekülasyon için güvenli olup olmadığını iki kez düşünün.

Sunucu Tarafında Tespit: Sec-Purpose

Spekülatif istekler Sec-Purpose başlığıyla gelir:

Sec-Purpose: prefetch              ← prefetch isteği
Sec-Purpose: prefetch;prerender    ← prerender isteği

Sunucunuz bu başlığa göre davranabilir. SvelteKit’te bir handle hook’u ile:

// src/hooks.server.js
export async function handle({ event, resolve }) {
  const secPurpose = event.request.headers.get("sec-purpose") ?? "";
  const spekulatif = secPurpose.includes("prefetch");

  // Spekülatif isteklerde sunucu tarafı sayaçları artırma
  if (!spekulatif) {
    await ziyaretiKaydet(event.url.pathname);
  }

  // Bu rotanın spekülatif yüklenmesini tamamen engelle
  if (spekulatif && event.url.pathname.startsWith("/cikis")) {
    return new Response(null, { status: 503 });
  }

  return resolve(event);
}

2XX dışında bir yanıt kodu döndürmek spekülasyonu iptal eder. Ancak bu son çaredir. Daha iyi yaklaşım, spekülasyona izin verip yan etkiyi sayfa gerçekten görüntülendiğinde JavaScript ile tetiklemektir.

İstemci Tarafında Tespit: document.prerendering

Prerender edilen bir sayfa document.prerendering === true ile başlar. Kullanıcı gerçekten sayfaya geldiğinde prerenderingchange olayı tetiklenir.

Bunu bir promise’e sarmak en temiz kalıptır:

// Sayfa etkinleştiğinde çözülen bir promise
const etkinlestiginde = new Promise((resolve) => {
  if (document.prerendering) {
    document.addEventListener("prerenderingchange", resolve, { once: true });
  } else {
    resolve();
  }
});

async function analitigiBaslat() {
  await etkinlestiginde;
  // Analitiği burada başlatın
}

analitigiBaslat();

Bir sayfanın prerender edilip edilmediğini sonradan da anlayabilirsiniz:

function sayfaPrerenderEdildi() {
  return document.prerendering || self.performance?.getEntriesByType?.("navigation")[0]?.activationStart > 0;
}

activationStart, prerender’ın başlamasıyla sayfanın etkinleşmesi arasındaki süreyi verir. Sıfırdan büyükse sayfa prerender edilmiştir. DevTools konsolunda hızlıca test etmek için:

performance.getEntriesByType("navigation")[0].activationStart;

SvelteKit ile Birlikte Kullanım

SvelteKit’in zaten bir ön yükleme mekanizması var. Bu blogun app.html dosyasında şu satır duruyor:

<body data-sveltekit-preload-data="hover"></body>

Bu iki teknik birbirinin rakibi değil, tamamlayıcısıdır. Farkı anlamak önemli.

Özellikdata-sveltekit-preload-dataSpeculation Rules API
Ne getirir?Rotanın JS modülü ve load verisiTam HTML dokümanı, alt kaynaklar
Kim çalıştırır?SvelteKit router’ı (istemci navigasyonu)Tarayıcının kendisi
KapsamUygulama içi yumuşak navigasyonTam sayfa navigasyonu
JS maliyetiSvelteKit runtime’ı içindeSıfır
Kullanıcı tercihiHayırSave-Data, pil, bellek dikkate alınır

SvelteKit’in preload’u uygulama içindeyken hızlıdır. Speculation Rules ise kullanıcı siteye dışarıdan girdiğinde ya da tam sayfa yenilemesi olduğunda devreye girer. İkisini birlikte çalıştırın.

Kuralları eklemek için en basit yer src/app.html dosyasıdır; işaretleme statiktir ve tarayıcı kuralları ilk HTML içinde görür:

<body data-sveltekit-preload-data="hover">
  <script type="speculationrules">
    {
      "prerender": [
        {
          "where": {
            "and": [{ "href_matches": "/*" }, { "not": { "selector_matches": "[rel~=nofollow]" } }]
          },
          "eagerness": "moderate"
        }
      ]
    }
  </script>
  <div style="display: contents">%sveltekit.body%</div>
</body>

Kuralların rotaya göre değişmesi gerekiyorsa, bunun yerine +layout.svelte içinde onMount ile ve bir sonraki bölümde gösterildiği gibi document.createElement kullanarak enjekte edin.

Dikkat: Speculation rules script’ini innerHTML ile eklemek güvenlik nedeniyle çalışmaz. Aynı şekilde DevTools Elements panelinden elle eklemek de kuralları kaydetmez. Dinamik ekleme yapacaksanız document.createElement kullanın.

Dinamik Ekleme ve Özellik Algılama

Speculation Rules henüz Baseline değil — MDN’de “sınırlı kullanılabilirlik” olarak işaretli ve pratikte Chromium tabanlı tarayıcılarda çalışıyor. Destekleyen tarayıcılarda modern API’yi, diğerlerinde eski <link rel="prefetch"> yöntemini kullanabilirsiniz:

if (HTMLScriptElement.supports?.("speculationrules")) {
  const script = document.createElement("script");
  script.type = "speculationrules";
  script.textContent = JSON.stringify({
    prerender: [{ where: { href_matches: "/*" }, eagerness: "moderate" }],
  });
  document.body.append(script);
} else {
  const link = document.createElement("link");
  link.rel = "prefetch";
  link.href = "/sonraki-yazi";
  document.head.append(link);
}

Desteklemeyen tarayıcılarda hiçbir şey bozulmaz. Bu yüzden Speculation Rules’ı bir aşamalı geliştirme (progressive enhancement) olarak düşünün: destekleyen kullanıcılar anında geçiş yaşar, diğerleri her zamanki deneyimi alır.

Core Web Vitals Üzerindeki Etkisi

Tam olarak prerender edilmiş bir sayfa etkinleştiğinde, Chrome metrikleri etkinleşme anına göre ölçer, prerender’ın başladığı ana göre değil. Sonuç:

  • LCP neredeyse sıfıra iner — en büyük öğe kullanıcı sayfayı görmeden önce render edilmiştir
  • CLS düşer — yükleme kaynaklı düzen kaymaları görünmez sekmede gerçekleşir
  • INP iyileşir — JavaScript kullanıcı etkileşime girmeden önce çalışmayı bitirmiştir

Bu, Chrome Kullanıcı Deneyimi Raporu’na (CrUX) yansır ve dolayısıyla Google’ın sıralamada kullandığı saha verisini doğrudan etkiler.

web-vitals kütüphanesinin 3.1.0 ve sonraki sürümleri prerender edilmiş navigasyonları Chrome ile aynı şekilde ele alır ve Metric.navigationType alanında işaretler. Ölçüm yaparken bu sürümde olduğunuzdan emin olun.

Prerender Oranınızı Ölçün

Kaç navigasyonun prerender edildiğini analitiğinize özel bir boyut olarak gönderin. Yatırımın karşılığını ancak böyle görürsünüz:

analitigeGonder({
  olay: "sayfa_goruntuleme",
  prerenderEdildi: sayfaPrerenderEdildi(),
});

Sık Karşılaşılan Tuzaklar

1. Bayat içerik

Chrome prefetch edilen sayfaları yaklaşık 5 dakika önbellekte tutar. Kullanıcı 5 dakika öncesine ait içerik görebilir. Hızla değişen sayfalarda (canlı skor, stok durumu, yorum listesi) bunu hesaba katın. Gerekirse Clear-Site-Data başlığıyla önbelleği temizleyin:

Clear-Site-Data: "prefetchCache", "prerenderCache"

Bu başlığı durum değiştiren herhangi bir aynı site isteğinde döndürebilirsiniz — örneğin /api/sepete-ekle çağrısında.

2. Kullanıcıya özel durum uyuşmazlığı

Kullanıcı sekme 1’de giriş yaparken sekme 2’de çıkış yapmış haldeki bir sayfa prerender edilmişse, o sayfaya geçtiğinde kendisini çıkış yapmış görür. Çözüm: sayfaların kendilerini tazelemesi. Broadcast Channel API bu senaryo için idealdir.

3. Content Security Policy

Speculation rules bir <script> elementi kullandığı için, CSP uyguluyorsanız script-src direktifinde izin vermeniz gerekir. 'inline-speculation-rules' kaynağı, hash veya nonce kullanabilirsiniz. Katı CSP kullanan siteler kuralları JavaScript ile enjekte etmelidir.

4. SPA’larda çalışmaz

Speculation Rules yalnızca tarayıcının yönettiği tam sayfa navigasyonları için geçerlidir. Bir SPA’nın kendi içindeki rota değişiklikleri prerender edilemez. Ancak SPA’nın kendisini önceki bir sayfadan prerender ederek ilk yükleme maliyetini karşılayabilirsiniz.

5. UTM parametreleri önbelleği bölüyor

?utm_content=123 ve ?utm_content=456 sunucudan aynı sayfayı döndürüyorsa, No-Vary-Search ile tarayıcıya bunu söyleyin:

<script type="speculationrules">
  {
    "prefetch": [{ "urls": ["/urunler"], "expects_no_vary_search": "params=("id")" }]
  }
</script>

Uygulama Kontrol Listesi

Aşamalı ve güvenli bir dağıtım için sırayla ilerleyin:

  1. Prefetch ile başlayın. Risk düşük, kazanç gerçek. moderate eagerness ile doküman kuralı yazın.
  2. Güvensiz URL’leri hariç tutun. Çıkış, dil değiştirme, sepet, OTP ve kota tüketen rotaları not bloğuna ekleyin.
  3. Analitiği koruyun. document.prerendering kontrolünü ekleyin, aksi halde sayfa görüntüleme sayılarınız şişer.
  4. DevTools ile doğrulayın. Application → Speculative loads panelinde hangi kuralların tetiklendiğini ve neden iptal edildiğini görebilirsiniz.
  5. Ölçün. activationStart değerini analitiğinize gönderin ve isabet oranınızı takip edin.
  6. Sonra prerender’a geçin. Yalnızca isabet oranı yüksek rotalar için ve yine moderate ile.

Sıkça Sorulan Sorular

Speculation Rules API hangi tarayıcılarda çalışıyor?

Chrome 109’dan itibaren prerender, eagerness alanı Chrome 121’den itibaren destekleniyor. Chromium tabanlı tarayıcılar (Edge, Opera, Brave) da destekliyor. Safari ve Firefox henüz desteklemiyor; MDN bu nedenle özelliği “Baseline değil” olarak işaretliyor. Destek olmadığında hiçbir şey bozulmaz, sayfa normal şekilde yüklenir.

Prefetch mi prerender mı kullanmalıyım?

Prefetch ile başlayın. Maliyeti bir GET isteği kadardır ve geniş çapta uygulanabilir. Prerender’ın maliyeti bir <iframe> render etmeye yakındır; yalnızca kullanıcının o sayfaya gitme olasılığı gerçekten yüksek olduğunda kullanın.

Prerender kullanıcının verisini boşa harcar mı?

Kullanıcı o sayfaya gitmezse evet. Ancak Chrome, Save-Data modunda, pil tasarrufunda, düşük bellekli cihazlarda ve kullanıcı “sayfaları önceden yükle” ayarını kapattığında spekülasyon yapmaz. Yine de eagerness seçiminizi bilinçli yapın — immediate yerine moderate çoğu durumda doğru dengedir.

Prerender analitiğimi bozar mı?

Önlem almazsanız evet — kullanıcı hiç görmediği sayfalar için görüntüleme kaydedilir. Çözüm, document.prerendering kontrolü veya prerenderingchange olayıyla analitiği ertelemektir. Google Analytics ve NewRelic gibi bazı sağlayıcılar prerender’ın farkındadır, ancak kendi kodunuzu doğrulayın.

data-sveltekit-preload-data varken buna gerek var mı?

Evet, farklı şeyler yapıyorlar. SvelteKit’in preload’u uygulama içi yumuşak navigasyonlar için rota modülünü ve load verisini getirir. Speculation Rules tarayıcı seviyesinde tam doküman navigasyonunu hedefler ve kullanıcının veri/pil tercihlerine saygı gösterir. İkisini birlikte kullanın.

Speculation rules Core Web Vitals puanımı gerçekten iyileştirir mi?

Evet. Chrome, prerender edilmiş sayfaların metriklerini etkinleşme anına göre ölçer; bu da genellikle sıfıra yakın bir LCP demektir. Bu değerler CrUX’a yansır ve Google’ın sıralamada kullandığı saha verisidir. Ancak yalnızca gerçekten prerender edilen navigasyonlar için geçerlidir; isabet oranınızı ölçmeniz gerekir.

<link rel="prerender"> ile farkı ne?

<link rel="prerender"> hiçbir zaman standartlaşmadı, yalnızca Chrome’da vardı ve artık NoState Prefetch davranışına indirgenmiş durumda. Speculation Rules ise JavaScript ile yüklenen alt kaynakları da getirir, Cache-Control ayarları tarafından engellenmez ve tarayıcının reddedebileceği bir ipucu olarak davranır.

Sonuç

Speculation Rules API, performans optimizasyonunda alışık olmadığımız bir şey sunuyor: JavaScript paketinizi büyütmeden, mimarinizi değiştirmeden elde edilen büyük bir kazanç. Bir JSON bloğu yazıyorsunuz ve tarayıcı gerisini hallediyor.

Ama bedava değil. Bedeli dikkat: hangi URL’lerin yan etkisi olduğunu bilmek, analitiğinizi prerender’a hazırlamak ve eagerness ayarını kullanıcılarınızın kaynaklarına saygılı seçmek.

moderate eagerness ile bir prefetch kuralından başlayın. Çıkış ve durum değiştiren rotaları hariç tutun. Analitiğinizi koruyun. Bir hafta ölçün. Sayılar sizi ikna ederse prerender’a geçin.

Kullanıcınızın tıklamadan önce beklemeye başlamadığı her sayfa, kazandığınız bir kullanıcıdır.

entr