Neden Google Görmüyor? React, Firebase, Cloudflare Rehberi

Google Neden Yeni Makalelerini Görmüyor? React, Firebase ve Cloudflare ile Otomatik İndeksleme Sistemi

Google'ın yeni makalelerinizi görmemesinin en yaygın sebebi, sitemap.xml dosyanızın statik olmasıdır. Bu dosya yalnızca site build edildiği anda bir kere oluşturulur ve yeni bir makale yayınladığınızda otomatik olarak güncellenmez. Google'ın crawler'ı sitemap'i her ziyaret ettiğinde aynı eski URL listesini görür, yeni makalenizin varlığından haberi olmaz. Çözüm, sitemap'i her istek geldiğinde anlık olarak veritabanından üreten dinamik bir yapı kurmaktır. Bu makalede, React tabanlı bir blog için Cloudflare Pages Functions ve Firebase Realtime Database kullanarak bu sistemi sıfırdan nasıl kuracağınızı, hangi hataların sizi nasıl tuzağa düşürdüğünü ve kurduktan sonra ne yapmanız gerektiğini adım adım göreceksiniz.

Statik Sitemap Sorunu Aslında Nedir?

React ile kurulan çoğu blog, build sürecinde bir defaya mahsus sitemap.xml dosyası üretir. Bu dosya genelde bir script tarafından, o anki makale listesine bakılarak oluşturulur ve statik bir dosya olarak sunucuya yüklenir. Sorun şu: siz yeni bir makale yayınladığınızda, site yeniden build edilmediği sürece sitemap hiç değişmez. Üç gün önce yayınladığınız makale, sitemap.xml'de hâlâ yer almıyor olabilir; bir ay önce yayınladığınız makale de aynı şekilde eksik kalabilir.

Bu durum özellikle düzenli içerik üreten bloglar için ciddi bir kayıp anlamına gelir. Google, sitemap'i bir keşif sinyali olarak kullanır; sitemap'te olmayan bir sayfa ya hiç keşfedilmez ya da çok daha yavaş keşfedilir, çünkü Google o sayfaya ancak başka bir yoldan (iç bağlantı, harici backlink) ulaşırsa fark eder. Yeni kurulan bir blog için bu yollar henüz zayıftır, dolayısıyla sitemap pratikte tek güvenilir keşif kanalı haline gelir.

Daha önce blogun teknik altyapısını React ve Cloudflare üzerine kurarken bu build-time/runtime ayrımına farklı bir bağlamda değinmiştik (React Server Components (RSC) ve Edge Fonksiyonları ile AI Agent Entegrasyonu); sitemap konusunda da aynı ayrım belirleyici.

İşin can alıcı noktası, bu sorunun fark edilmesinin de zor olması. Site teknik olarak kusursuz çalışır, sayfalar tarayıcıdan erişilebilirdir, ama Search Console'da "Keşfedildi - şu anda dizine eklenmedi" durumundaki makale sayısı sürekli artar. Çoğu yayıncı bu sorunu aylar sonra, beklediği organik trafik bir türlü gelmeyince fark eder; o noktada da geriye dönük kaybedilen zamanı telafi etmek mümkün olmaz.

Bir diğer gözden kaçan nokta da şu: build script'i sitemap'i üretirken genelde "build anındaki" veri kaynağını kullanır. Eğer içerik veritabanınız (bu örnekte Firebase) ile build script'iniz arasında bir senkronizasyon gecikmesi varsa, sitemap zaten en baştan eksik doğabilir. Yani statik sitemap sadece "güncellenmiyor" değil, ilk üretildiği anda da hatalı olabilir.

Google Yeni Sayfaları Nasıl Buluyor?

Google'ın bir sayfayı keşfetme yöntemleri sınırlıdır: sitemap.xml üzerinden, başka bir sayfadaki iç bağlantı üzerinden, ya da harici bir backlink üzerinden. Yeni kurulan ve henüz çok fazla backlink almamış bir blog için bu üçünden en güvenilir ve en hızlı olanı sitemap'tir; diğer iki yol zamanla gelişir ama başlangıçta üzerine inşa edebileceğiniz bir temel sunmaz.

Crawl budget kavramı da burada devreye giriyor. Google, her site için sınırlı bir tarama bütçesi ayırır; bu bütçe sitenin otoritesine, sunucu yanıt hızına ve geçmiş tarama davranışına göre belirlenir. Sitemap güncel değilse, Google'ın crawler'ı zaten kıt olan bu bütçeyi eski, değişmeyen sayfaları tekrar tekrar kontrol etmekle harcar; yeni sayfalarınızı bulmaya zaman ayırmaz. Dinamik bir sitemap, Google'a her ziyarette "işte güncel liste, en son eklenenler bunlar" demenin en net yoludur ve crawl budget'ın doğru yere harcanmasını sağlar.

Burada bir yanlış anlaşılmaya da değinmek gerekiyor: sitemap'e eklemek, otomatik dizine eklenme garantisi vermez. Sitemap sadece "bu sayfa var, lütfen kontrol et" sinyalidir; Google yine de sayfanın kalitesine, içerik bütünlüğüne ve teknik erişilebilirliğine göre dizine alıp almamaya kendi karar verir. Ama sinyal hiç gitmiyorsa, karar verme süreci hiç başlamaz. Dinamik sitemap, sizi bu sürecin en azından başlangıç noktasına taşır.

Çözüm: İstek Anında Oluşan Dinamik Sitemap

Kurulacak mimari aslında basit: kullanıcı veya Google'ın crawler'ı /sitemap.xml adresine her istek attığında, sunucu tarafında çalışan bir fonksiyon devreye girer, Firebase Realtime Database'den o anki tüm makale listesini çeker, bunları standart sitemap XML formatına dönüştürür ve anında yanıt olarak döner. Hiçbir build adımı, hiçbir manuel güncelleme gerekmez; sistem her zaman o anki gerçek veriyi yansıtır.

Cloudflare Pages Functions burada tam ihtiyacımız olan şey: projenizdeki functions klasörüne sitemap.xml.js adında bir dosya koyduğunuzda, Cloudflare otomatik olarak bunu /sitemap.xml adresine route eder. Ekstra bir routing yapılandırmasına gerek yok, dosya tabanlı yönlendirme kendiliğinden çalışır. Bu, Express veya başka bir sunucu çatısı kurmadan, doğrudan edge'de çalışan minimal bir API endpoint'i elde etmenizi sağlar.

Aşağıda bu fonksiyonun tam kodu var. Kod, Firebase'den veri çekiyor, her makale için bir url bloğu üretiyor ve hepsini birleştirip XML olarak döndürüyor. (Kod bloğu formatını netleştirene kadar aşağıdaki bölümü düz metin işaretleyiciyle ayırdım.)


dosya: functions/sitemap.xml.js

export async function onRequest(context) {
  const firebaseUrl =
    "https://SIZIN-PROJE-ID.firebaseio.com/articles.json";

  const response = await fetch(firebaseUrl);
  const data = await response.json();

  const baseUrl = "https://neondijital.com";
  let urls = "";

  for (const key in data) {
    const article = data[key];
    const slug = article.slug;
    const lastmod = article.publishedAt
      ? article.publishedAt.split("T")[0]
      : "";

    urls += "  <url>\n";
    urls += "    <loc>" + baseUrl + "/blog/" + slug + "</loc>\n";
    urls += "    <lastmod>" + lastmod + "</lastmod>\n";
    urls += "  </url>\n";
  }

  const sitemap =
    "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
    "<urlset xmlns=\"http://www.sitemaps.org/schemas/sitemap/0.9\">\n" +
    urls +
    "</urlset>";

  return new Response(sitemap, {
    headers: {
      "Content-Type": "application/xml",
      "Cache-Control": "public, max-age=3600",
    },
  });
}

Burada Cache-Control başlığına dikkat edin: max-age=3600 değeri, Cloudflare'in bu yanıtı bir saat boyunca edge'de cache'lemesine izin veriyor. Bu, her ziyarette Firebase'e gereksiz istek atılmasını önlerken, yeni bir makale yayınladıktan en fazla bir saat sonra sitemap'in güncellenmesini garantiliyor.

Firebase Tarafında Neye İhtiyacınız Var?

Bu sistemin çalışması için Firebase Realtime Database'de makalelerinizin belirli iki alanı tutması gerekiyor: slug ve publishedAt (veya yayın tarihini tutan herhangi bir alan). Aşağıdaki gibi basit bir veri yapısı yeterli:


veri yapısı: Firebase RTDB

{
  "articles": {
    "makale1": {
      "slug": "ornek-makale-basligi",
      "title": "Örnek Makale Başlığı",
      "publishedAt": "2026-06-10T09:00:00Z"
    },
    "makale2": {
      "slug": "ikinci-makale",
      "title": "İkinci Makale",
      "publishedAt": "2026-06-12T09:00:00Z"
    }
  }
}

Eğer mevcut veri yapınızda bu alan adları farklıysa (örneğin slug yerine url, publishedAt yerine date), yukarıdaki fonksiyon kodundaki article.slug ve article.publishedAt satırlarını kendi alan adlarınızla değiştirmeniz yeterli. Önemli olan, her makale kaydının bu iki bilgiye node içinde erişilebilir şekilde sahip olması.

Firebase tarafında dikkat edilmesi gereken bir nokta da güvenlik kuralları. Veritabanınız tamamen kapalıyken bu REST isteği başarısız olur. articles node'u için en azından okuma iznini herkese açık yapmanız, ya da fonksiyonunuzdan özel bir auth token ile erişim sağlamanız gerekir. Çoğu blog senaryosunda makale listesi zaten herkese açık bilgi olduğundan, salt okunur erişim güvenlik açısından sorun yaratmaz; yazma izinlerini kapalı tutmanız yeterlidir.

Cloudflare Pages Functions Nasıl Devreye Giriyor?

Cloudflare Pages projelerinde functions klasörü özel bir konuma sahiptir. Projenizin kök dizininde functions/sitemap.xml.js dosyasını oluşturduğunuzda, Cloudflare deploy sırasında bunu otomatik olarak algılar ve gelen /sitemap.xml isteklerini bu fonksiyona yönlendirir. Statik dosyalarla (örneğin public klasöründeki eski sitemap.xml) bir isim çakışması varsa, functions klasöründeki dinamik versiyon her zaman önceliklidir; bu yüzden eski statik dosyayı tamamen silmeniz en güvenli yoldur.

Deploy ettikten sonra yapmanız gereken tek şey, tarayıcınızdan doğrudan https://neondijital.com/sitemap.xml adresine gidip yanıtın güncel makale listesini içerdiğini doğrulamak. NeonCore'un otomatik teslimat sistemini kurarken benzer bir Cloudflare Worker mimarisi üzerinde çalışmıştık; oradaki webhook mantığı ile buradaki sitemap mantığı aynı prensibe dayanıyor (NeonCore otomatik teslimat sistemi). Firebase'e her istek için canlı bir fetch çağrısı yapıldığından, Firebase tarafındaki okuma kotanızı da göz önünde bulundurun; ücretsiz Spark planında bu çoğu blog için sorun yaratmaz, ama yüksek trafikli bir sitede Cache-Control süresini buna göre ayarlamak mantıklıdır.

Performans ve Maliyet: Cache Süresini Nasıl Belirlemelisiniz?

Cache-Control süresi, bu sistemin tek ayar gerektiren noktasıdır. Süre ne kadar kısa olursa sitemap o kadar güncel olur, ama Firebase'e o kadar sık istek gider. Süre ne kadar uzun olursa Firebase maliyeti düşer, ama yeni yayınladığınız makalenin sitemap'e yansıması o kadar gecikir.

Günde birden fazla makale yayınlayan bir blog için 600-900 saniye (10-15 dakika) aralığı iyi bir denge sunar. Haftada birkaç makale yayınlayan bir tempoda ise 3600 saniye (1 saat) hem maliyeti düşürür hem de pratikte fark edilir bir gecikme yaratmaz. Hiç cache kullanmamak (no-store) teorik olarak en güncel sonucu verir ama her crawler ziyaretinde, her sayfa yenilemesinde Firebase'e istek gitmesi anlamına gelir; bu, ölçeklendiğinizde gereksiz bir maliyet kalemi olur.

Alternatif Yöntem: Build Webhook'u Neden Yeterli Değil?

Bazı ekipler bu sorunu, makale yayınlandığında bir webhook tetikleyip siteyi yeniden build ettirerek çözmeye çalışır. Bu yöntem çalışır, ama iki ciddi dezavantajı vardır. Birincisi, her yeni makale için tam bir build süreci tetiklemek, özellikle büyüyen bir site için zamanla yavaşlar ve build dakikası maliyetini artırır. İkincisi, build süreci başarısız olursa (bağımlılık hatası, geçici bir API kesintisi) sitemap güncellenmeden kalır ve bunu fark etmeniz build loglarını takip etmenize bağlıdır.

İstek anında çalışan dinamik fonksiyon yaklaşımı bu iki sorunu da ortadan kaldırır: hiçbir build tetiklenmez, hiçbir uzun süreli süreç çalışmaz, sadece milisaniyeler içinde tamamlanan bir veri okuma ve XML üretme işlemi vardır. Bu yüzden, özellikle sık içerik yayınlayan bloglar için build webhook yöntemini değil, bu makaledeki yaklaşımı tercih etmenizi öneririm.

Google Search Console'a Bildirme

Dinamik sitemap'iniz hazır olduğunda, Google'a bunu doğrudan bildirmeniz gerekir. Search Console'da Sitemaps bölümüne girip https://neondijital.com/sitemap.xml adresini ekleyin. Google bu noktadan sonra sitemap'i düzenli aralıklarla kendi başına yeniden çekecek; siz hiçbir manuel işlem yapmadan yeni makaleleriniz otomatik olarak listeye dahil olacaktır.

İlk gönderimden sonra Search Console'un sitemap'i işlemesi birkaç saatten birkaç güne kadar sürebilir. Bu süre boyunca "Başarılı" durumunu görmeseniz bile telaşlanmayın; önemli olan, sitemap'in döndürdüğü URL sayısının gerçek makale sayınızla eşleşip eşleşmediğidir. Eşleşmiyorsa, fonksiyon kodunda veya Firebase veri yapısında bir uyumsuzluk olduğu anlamına gelir ve bunu bir sonraki bölümdeki test adımlarıyla teşhis edebilirsiniz.

Test Etme ve Doğrulama

Yayına almadan önce fonksiyonun doğru çalıştığını doğrulamanın en hızlı yolu, tarayıcıdan /sitemap.xml adresine gitmek ve dönen XML'in geçerli olup olmadığını kontrol etmektir. XML formatı hatalıysa (örneğin kapanmayan bir etiket varsa) Google bu sitemap'i tamamen reddeder, kısmen değil; bu yüzden tek bir hatalı satır bile tüm sitemap'i geçersiz kılabilir.

Yeni bir makale ekleyip Firebase'deki kaydı oluşturduktan sonra, sitemap'i yeniden ziyaret edin (Cache-Control süresi geçtikten sonra) ve yeni URL'nin listede göründüğünü doğrulayın. Bu test, sisteme tam güvenmeden önce mutlaka yapılması gereken bir adımdır; özellikle alan adı eşleştirmesinde (slug/publishedAt) yapılan küçük bir yazım hatası, sessizce makalelerinizin sitemap'ten atlanmasına yol açabilir ve bunu fark etmeniz haftalar sürebilir.

Sık Yapılan Hatalar

En sık görülen hata, lastmod alanının yanlış formatta dönmesidir. Sitemap protokolü ISO 8601 tarih formatı bekler (YYYY-AA-GG); Firebase'deki publishedAt alanınız farklı bir formatta tutuluyorsa, split işlemi öncesinde formatı düzeltmeniz gerekir, aksi halde Google bu alanı tamamen göz ardı edebilir.

İkinci yaygın hata, Cache-Control başlığının hiç eklenmemesidir; bu durumda her istek Firebase'e gider ve gereksiz maliyet ile gecikme yaratır. Üçüncü hata, eski statik sitemap.xml dosyasının public klasöründe unutulmasıdır; bu dosya silinmediği sürece bazı Cloudflare yapılandırmalarında çakışma yaşanabilir ve fonksiyonun devreye girip girmediğini gerçek bir istekle doğrulamadan anlamak mümkün olmaz.

Dördüncü hata, Firebase güvenlik kurallarının articles node'u için okuma erişimini tamamen kapatmasıdır; bu durumda fonksiyon sessizce boş bir sitemap döndürür ve hata mesajı görmediğiniz için sorunu fark etmeniz uzun sürer. Beşinci hata ise slug alanlarında Türkçe karaktere veya boşluğa izin verilmesidir; URL-uyumsuz karakterler sitemap'te geçersiz bağlantılara yol açar, bu yüzden slug üretim aşamasında karakterleri her zaman normalize etmeniz gerekir.

Bu Sistemin Sınırları Neler?

Bu yaklaşım sitemap'i her zaman güncel tutar, ama Google'ın bir sayfayı ne zaman tarayacağına doğrudan müdahale edemez. Sitemap, bir öncelik sinyali değil bir keşif sinyalidir; çok yüksek öncelikli bir sayfanın hızlı indekslenmesini istiyorsanız Search Console üzerinden manuel URL inceleme talebi göndermek bu sistemle birlikte kullanılabilir, onun yerine geçmez.

Ayrıca bu sistem, Firebase'in erişilebilir olmasına bağımlıdır; Firebase tarafında geçici bir kesinti yaşanırsa, o anda gelen sitemap isteği boş veya hatalı dönebilir. Production ortamında bu riski azaltmak için fonksiyona basit bir hata yakalama (try/catch) eklemeniz ve Firebase isteği başarısız olduğunda en azından mevcut cache'lenmiş sürümü döndürmeniz önerilir.

Bir diğer pratik sınır, fonksiyonun hata durumunda kullanıcıya (veya Google'a) ne döndüreceğidir. Firebase isteği zaman aşımına uğrarsa veya beklenmedik bir formatta veri dönerse, fonksiyonun çökmesi ya da boş bir yanıt vermesi yerine, en azından geçerli ama kısa bir sitemap döndürmesi tercih edilir. Bunun için fonksiyona basit bir try/catch bloğu eklemek ve hata durumunda son bilinen makale listesini (örneğin önceden cache'lenmiş bir yedek) döndürmek, sistemin tamamen sessiz kalmasını önler. Bu küçük önlem, üretim ortamında yaşanabilecek geçici kesintilerin sitemap'inizi tamamen devre dışı bırakmasını engeller.

Bundan Sonra Ne Yapmalısınız?

Sistem kurulduktan sonra yapmanız gereken tek düzenli iş, yeni makale yayınladığınızda Firebase'e slug ve yayın tarihi bilgisinin doğru şekilde yazıldığından emin olmaktır. Bu adım zaten yayın iş akışınızın bir parçasıysa, ekstra bir yük getirmez. İsterseniz bir adım daha ileri gidip Google Search Console API üzerinden her yeni makale için manuel indeksleme isteği de gönderebilirsiniz; ama dinamik sitemap tek başına, indeksleme hızınızda gözle görülür bir iyileşme sağlayacaktır.

Bu Yapıyı Mevcut Yayın Akışınıza Nasıl Entegre Edersiniz?

Teknik kurulum bittikten sonra asıl mesele, bu sistemin günlük yayın alışkanlıklarınızla sürtüşmeden çalışmasıdır. Eğer makalelerinizi zaten bir admin paneli üzerinden Firebase'e yazıyorsanız, yapmanız gereken tek şey o panelin slug ve publishedAt alanlarını eksiksiz ve doğru formatta kaydettiğinden emin olmaktır; bu genelde formun bir doğrulama adımı eklemekten ibarettir. Slug alanını otomatik üretip elle düzenlemeye izin vermek, hem tutarlılığı korur hem de Türkçe karakter veya boşluk gibi hataların önüne geçer.

Eğer birden fazla kişi içerik yayınlıyorsa, slug çakışmalarına karşı bir kontrol mekanizması eklemek faydalı olur; aynı slug'a sahip iki makale, sitemap'te aynı URL'nin iki kez görünmesine ve Google'ın bunu bir teknik hata olarak değerlendirmesine yol açabilir. Basit bir "bu slug zaten kullanılıyor" kontrolü, yayın öncesi panelde gösterilebilir.

Taslak halinde tutulan, henüz yayınlanmamış makalelerin de aynı Firebase node'unda saklanması durumunda, fonksiyonun bu taslakları filtrelemesi gerekir. Bunun için article kaydına bir status veya published alanı eklemek ve fonksiyon içinde sadece status değeri "published" olan kayıtları sitemap'e dahil etmek yeterlidir; aksi halde henüz yayına alınmamış içerikler de Google'a görünür hale gelir ve bu hem kafa karıştırıcı hem de istenmeyen bir durumdur.

Son olarak, sistemi kurduktan sonraki ilk haftada Search Console'daki Sayfa İndeksleme raporunu birkaç gün arayla kontrol etmek, beklenen iyileşmenin gerçekleşip gerçekleşmediğini görmenizi sağlar. Yeni yayınladığınız makalelerin "Keşfedildi" durumundan "Dizine Eklendi" durumuna geçiş hızı, bu kurulumun gerçek etkisini ölçmenin en net yoludur.

Sık Sorulan Sorular

Dinamik sitemap, statik sitemap'ten SEO açısından daha mı iyidir? Evet, çünkü Google her zaman güncel URL listesine erişir. Statik sitemap'te yeni sayfalar build yapılana kadar görünmez; dinamik sitemap'te bu gecikme tamamen ortadan kalkar ve Google'a sürekli güncel bir keşif sinyali gider.

Bu sistemi Cloudflare Pages dışında bir platformda kurabilir miyim? Evet, mantık aynı kalır. Vercel'de bir API route, Netlify'da bir serverless function ile aynı fonksiyonu yazabilirsiniz; önemli olan isteğin anlık olarak veritabanına gidip güncel listeyi dönmesidir, kullandığınız platformun adı sonucu değiştirmez.

Firebase yerine başka bir veritabanı kullanıyorsam ne değişir? Sadece fetch çağrısının hedef URL'si ve veriyi okuma şekli değişir. Yapının genel mantığı, yani anlık veri çekme, XML üretme ve cache başlığı ekleme, tamamen aynı kalır; PostgreSQL, MongoDB veya başka bir veritabanı için sadece sorgu kısmını uyarlamanız yeterlidir.

Sitemap'teki URL sayısı için bir sınır var mı? Standart sitemap protokolü tek dosyada 50.000 URL'ye kadar destekler. Bir blog için bu sayıya ulaşmak genelde yıllar alır, ama ulaşırsanız sitemap'i birden fazla dosyaya bölüp bir sitemap index dosyası oluşturmanız gerekir.

Cache-Control süresini ne kadar kısa tutmalıyım? Çoğu blog için 3600 saniyelik bir süre iyi bir dengedir. Günde birden fazla makale yayınlıyorsanız bu süreyi 600-900 saniyeye çekebilirsiniz; trafiğiniz düşükse Firebase maliyetini daha da azaltmak için süreyi 6 saate kadar çıkarabilirsiniz.