Göktay Logo
Tüm Yazılar

Astro.js ve Teknik SEO: Sitenin Hızlı Olması Google’ı Neden İkna Etmedi?

6 dk okuma

Giriş: Bir Geliştiricinin İllüzyonu

Her şey kusursuz görünüyordu. Bir Tech Lead olarak sitemi en modern teknolojilerle, Go ve Astro.js kullanarak inşa etmiştim. Build alıyordum, Lighthouse testine sokuyordum ve karşımda dört tane yeşil 100 puan görüyordum. Performans mükemmeldi, erişilebilirlik tamdı. Kendi kendime “SEO işi bitti” diyordum.

Ancak Google Search Console (GSC) paneline girdiğimde karşılaştığım tablo tam bir “korku filmi” gibiydi.

“Kullanıcı tarafından seçilen standart sayfa olmadan kopya”, “Yönlendirmeli sayfa”, “Tarandı - şu anda dizine eklenmiş değil”.

Yüzlerce sayfa Google’ın kapısından geri dönmüştü. Kodun hızı saniyelerle ölçülüyordu ama Google botu siteme girdiğinde kör bir adamın labirentte dolaşması gibi kayboluyordu. Sorun yazdığım Go API’lerinde veya React component’lerinde değildi. Sorun, binanın temeline, yani Semantik HTML ve DOM hiyerarşisine attığım yanlış imzalardaydı.

Bu yazıda, “hızlı kod iyi SEO’dur” yanılgısının beni nasıl bir teknik borç (technical debt) çukuruna sürüklediğini ve bu labirentten nasıl çıktığımı adım adım anlatacağım. Eğer siz de sitenizin <head> etiketlerini <body> içinde yüzdürüyorsanız, Google’ın neden sizi ciddiye almadığını şimdi anlayacaksınız.

Giriş: Sitenin Hızlı Olması Google’ı Neden İkna Etmedi?

Aydınlanma Anı: 30 Günlük Bekleyiş ve Acı Gerçek

Sitemi canlıya aldıktan sonra, “Google botu er ya da geç gelir, kodum zaten çok hızlı” diyerek rahat bir şekilde indexlenmeyi beklemeye başladım. Bu bekleyiş tam bir ay sürdü. GSC panelindeki “Dizine eklenmedi” sayıları artıyordu ama ben inatla “Yeni site olduğu içindir, zamanla düzelir” bahanesine sığınıyordum.

Ta ki bir gün, tarayıcımda sürekli açık duran ama detaylı incelemediğim o basit Chrome eklentisine (SEO META in 1 CLICK) alıcı gözüyle bakana kadar.

Farklı sayfalarda gezinirken eklentinin özet (Summary) sekmesinde hep aynı manzarayı görüyordum:

İşte o an, GSC panelinde gördüğüm o karmaşık hataların ne anlama geldiğini idrak ettim. Google sitemi yavaş olduğu için değil, kimliksiz ve yönsüz olduğu için reddediyordu. Bütün sayfalarım aynı SEO meta etiketlerine sahipti ve hiçbir sayfamda “Benim orijinal adresim budur” diyen bir Canonical etiketi yoktu.

Ekrana sadece bakmayı bırakıp, hataları bir Tech Lead gözüyle “okumaya” başladığımda, sorunun kaynak kodda değil, kurguladığım Astro mimarisinde (DOM hiyerarşisinde) olduğunu gördüm. Ve hızlıca refactoring sürecine başladım.

1. Çift Başlılık: “Single Source of Truth” İhlali ve DOM Katliamı

Yazılım dünyasında en temel prensiplerden biri Single Source of Truth (Tek Doğruluk Kaynağı) ilkesidir. Eğer bir veri birden fazla yerde tanımlanıyorsa, o sistemde kaos kaçınılmazdır. Ben ise bu prensibi, sanki M.Ö. 900 yılından kalma taş devri tekniklerini kullanıyormuşçasına kendi ellerimle ezdim geçtim.

Sitemin mimarisini kurgularken, her sayfa için ayrı meta datalar oluşturup bunları bir Layout şablonuna basmaya çalışmıştım. Daha sonra Astro’nun muhteşem astro-seo kütüphanesini projeye dahil ettim. Ancak eski, paslanmış ve kötü SEO yapımı temizlemeyi unuttum.

Teknik Borç (Technical Debt) Çukurunda Bir Gün

bcrypt-validator.astro veya cron-visualizer.astro gibi sayfalarımın kaynak koduna baktığınızda mimari bir cinayet işlendiğini görebilirdiniz:

<Layout title={pageTitle}>
    <BaseHead
        title={pageTitle}
        description={pageDescription}
        jsonLd={[bcryptSchema, faqSchema]}
    />
    <Navbar />
    </Layout>

Buradaki sorun ne mi? Layout bileşeni kendi içinde zaten bir <head> ve <body> etiketi barındırıyor. Ben ise <BaseHead /> bileşenini Layout’un içine koyarak, tüm meta etiketlerini, canonical URL’leri ve JSON-LD şemalarını sayfanın <body> etiketinin içine hapsettim.

Google Botunun Gözündeki Kaos

Google örümcekleri siteme geldiğinde karşılaştıkları manzara şuydu:

  1. <head> etiketi içinde Layout’tan gelen yetersiz meta veriler.
  2. <body> etiketi içinde, SEO eklentisinden gelen ama yanlış yere basılmış mükemmel meta veriler.
  3. Aynı sayfada iki farklı başlık (title) ve iki farklı açıklama (description).

Arama motoru botları için bu bir “karar verilemezlik” durumudur. “Bu sayfanın asıl sahibi kim? Hangi veriyi ciddiye almalıyım?” sorularına cevap bulamayan Google, en kolay yolu seçti: Tüm sayfalarımı “kopya içerik” olarak işaretledi ve dizine eklemeyi reddetti. Kendi yazdığım modern SEO modülü, eski ve hatalı mimarimin altında ezilip kalmıştı. Bir “Tech Lead” olarak, backend’de Go ile mikroservisler koştururken, frontend’in giriş kapısında DOM hiyerarşisini katletmiştim.

2. Semantik HTML Hatası: Erken Kapanan <main> Etiketi

İlk hatamızı çözdük diyelim; artık Google sitemizin <head> kısmını doğru okuyabiliyor. Peki ya sayfanın gövdesi?

Arama motoru botları (ve ekran okuyucu kullanan engelli bireyler) bir sayfaya girdiklerinde, o sayfanın asıl odak noktasının, yani “biricik” ve “değerli” içeriğinin nerede başladığını bilmek isterler. HTML5 ile hayatımıza giren <main> etiketi tam olarak bu işe yarar. Botlara şu mesajı verir: “Sayfanın asıl olayı burada dönüyor, burayı dikkatli oku.”

Ben ise kendi sayfalarımda (jwt-decoder, json-formatter vb.) bu etiketi bir SEO intiharına dönüştürmüştüm.

Makaleyi Boşlukta Sallandırmak

Yazdığım araçların altında, o araçların arkasındaki mühendislik mantığını anlatan, anahtar kelime açısından zengin ve SEO’nun belkemiği olan <article> (makale) bölümleri vardı. Ancak kodumu yazarken görsel yapıya (CSS/Tailwind) o kadar odaklanmıştım ki, <main> etiketini arayüz bileşeninden (UI Component) hemen sonra kapatmıştım.

<Layout>
    <Navbar />

    <main>
        <h1>JWT Decoder</h1>
        <JwtDecoderUI />
    </main> <article>
        <h2>JWT: Stateless Kimlik Doğrulama Mimarisinin Perde Arkası</h2>
        <p>Modern web uygulamalarında...</p>
    </article>

    <Footer />
</Layout>

Yıkıcı Etki: Google’ın Gözündeki “Değersiz” İçerik

Bu basit </main> kapanış hatası yüzünden Google’a teknik olarak şu yalanı söylüyordum: “Benim sayfamın asıl içeriği sadece JWT Decoder arayüzüdür. Aşağıdaki o uzun makale ise sayfanın ana konusu değil; tıpkı footer veya sidebar gibi önemsiz, ek bir içerik.”

Arama motorları <main> dışında kalan metinlere (özellikle <article> etiketi de ana gövdeden koparılmışsa) çok daha düşük bir ağırlık (weight) verir. Ben sayfalarca teknik detay, makale ve SSS (Sıkça Sorulan Sorular) yazmıştım ama Google algoritması bunları “yan içerik” olarak sınıflandırıp indexleme önceliğini düşürmüştü.

Çözüm çok basitti ama fark etmesi bir o kadar zordu: </main> kapanış etiketini kesip, <article> bileşeninin de sonuna, yani <Footer />’ın hemen üstüne taşıdım. Sayfanın görselinde hiçbir şey değişmedi, ancak Google botunun gözünde sayfamın “değerli içerik haritası” baştan sona yeniden çizildi.

3. Veri Akışı Kopukluğu: Statik Layout Tuzağı

Astro gibi bileşen tabanlı sistemlerde Layout kullanmak, her sayfada aynı Navbar ve Footer’ı tekrar yazmamak için harika bir yoldur. Ancak bu yapı, dikkatli kurgulanmadığında tüm sayfalarınızın birbirinin kopyası (duplicate) gibi görünmesine neden olan bir “veri tıkanıklığı” yaratır.

Varsayılan Değerlerin Sessiz İstilası

Mimariyi kurarken BaseHead içinde bir güvenlik ağı oluşturmuştum: Eğer bir sayfa açıklama (description) göndermezse, sistem otomatik olarak “Göktay Gürbüzer - Fullstack Developer” metnini basıyordu. Bu, ilk bakışta mantıklı bir “fallback” mekanizması gibi görünse de aslında Google’ı kandırmaya çalışıp yakalanmak gibiydi.

Hata tam olarak şurada patlak verdi: Blog sayfalarımı sarmalayan BlogLayout.astro, MDX dosyalarından başlığı alıyor ama diğer hayati verileri (açıklama, anahtar kelimeler, görseller) yolda düşürüyordu.

// HATALI YAPI: Veri burada tıkanıyor
const { frontmatter } = Astro.props;
---
<Layout title={frontmatter.title}> <slot />
</Layout>

Siz MDX dosyanızın içine ne kadar harika bir description yazarsanız yazın, bu veri Layout’a, oradan da BaseHead’e ulaşmadığı sürece Google’ın gördüğü tek şey, her blog yazısı için aynı olan o varsayılan açıklama cümlesiydi.

Dinamik Prop Zinciri ve MDX Entegrasyonu

Bu sorunu çözmek için mimariyi “yukarıdan aşağıya” tam bir veri akışına (Data Flow) geçirdik. Artık MDX dosyasındaki frontmatter alanı sadece bir başlık deposu değil, tüm SEO stratejisinin motoru haline geldi.

  1. MDX Seviyesi: Her yazıya özel description ve keywords alanları zorunlu kılındı.
  2. BlogLayout Seviyesi: Bu veriler yakalanıp Layout’a birer prop olarak paslandı.
  3. Layout Seviyesi: Gelen veriler BaseHead’e, oradan da astro-seo eklentisine iletildi.

Artık bir makale yazdığımızda, sistem o makaleye ait özgün özet metnini ve etiketleri (tags) otomatik olarak yakalayıp sayfanın tam tepesine, Google’ın okumayı en sevdiği yere yerleştiriyor. “Statik” olan şablonumuz, içindeki veriyle yaşayan “dinamik” bir organizmaya dönüştü.

4. Çöpe Atılan Fırsatlar: Yapısal Veri (Schema.org) Gücü

Eğer DOM ağacını temizlediyseniz ve verilerinizi dinamik olarak Layout bileşeninize kadar taşıyabildiyseniz, Google artık sayfanızı “okuyabilir” demektir. Ancak okumak, anlamak anlamına gelmez.

Arama motoru botları sandığımız kadar zeki değildir. Onlar muhteşem CSS animasyonlarınızı veya ustaca yazılmış paragraflarınızı bir insan gibi takdir etmezler. Onlar veri ararlar. Siz sayfanıza harika bir Sıkça Sorulan Sorular (SSS) bölümü eklersiniz, ancak Google için o sadece alt alta dizilmiş <h3> ve <p> etiketlerinden ibarettir.

Düz Metinden “Anlamsal” Veriye Geçiş

goktay.dev’deki araç sayfalarımın altına (örneğin Regex Tester veya Bcrypt Validator) kullanıcıların en çok aradığı teknik soruları ve cevaplarını uzun uzun yazmıştım. Amacım Google’ın “Kullanıcılar bunları da sordu” (People Also Ask) kutucuklarına girmekti. Günler geçti, trafik gelmedi. Neden? Çünkü fırsatı çöpe atmıştım. Google’a “Hey, bu bir sorudur ve altındaki de uzman bir cevaptır” dememiştim.

Bu noktada imdadımıza Schema.org standartları ve JSON-LD formatı yetişiyor.

  1. FAQPage Şeması: Sadece metin olarak duran SSS bölümlerimi bir JSON objesine dönüştürüp Layout üzerinden <head> etiketine bastım. Bu, arama motoruna o metinlerin sıradan bir paragraf değil, yapısal bir soru-cevap zinciri olduğunu söyler.
  2. BlogPosting Şeması: Blog sayfalarım (şu an okuduğunuz bu makale gibi) için yazarın kim olduğunu, yayınlanma tarihini ve özetini Google’ın dilinde anlatan veri yapıları kurdum.
  3. SoftwareApplication Şeması: Geliştirdiğim ücretsiz araçların birer “Web Uygulaması” olduğunu ve ücretsiz (price: "0") sunulduğunu algoritmaya fısıldadım.

Kodumu refactor ederken her aracıma ve blog yazıma özel JSON-LD nesneleri (object) tanımlamaya başladım:

// BlogPosting Şeması Örneği
const blogPostingSchema = {
    "@context": "https://schema.org",
    "@type": "BlogPosting",
    "headline": frontmatter.title,
    "description": frontmatter.description,
    "author": {
        "@type": "Person",
        "name": "Göktay Gürbüzer"
    }
};

Görünürlük (Visibility) Patlaması

Bu verileri dinamik prop zincirimizle sayfanın <head> kısmına <script type="application/ld+json"> olarak eklediğimde, sihir gerçekleşti.

Arama sonuçlarında sadece mavi bir link olarak çıkmak yerine, sitemin altında geniş Soru-Cevap akordeonları belirlemeye başladı. Arama motoru artık içeriğimi sadece indekslemiyor, onu “anlamlandırarak” zengin snippet’lar (Rich Snippets) halinde kullanıcılara sunuyordu. Temiz kod (Clean Code) sadece backend mimarisinde değil, arama motoruyla kurduğunuz iletişimde de şarttı.

SEO Bir İçerik Değil, Mimari Meselesidir

Backend veya full-stack geliştiriciler olarak genellikle kompleks iş mantıklarına, mikroservis haberleşmelerine ve veritabanı optimizasyonlarına odaklanırız. İş HTML ve SEO’ya geldiğinde ise çoğu zaman “iki meta etiket ekler, başlığı basar geçerim” mantığıyla hareket ederiz. goktay.dev’i ayağa kaldırırken benim de düştüğüm en büyük tuzak buydu.

Google’ın botları, arka planda ne kadar kusursuz bir Go kodu yazdığınızla veya React bileşenlerinizin ne kadar optimize render edildiğiyle ilgilenmez. Onlar için web, semantik ve hiyerarşik bir veri ağacıdır (DOM). Eğer veritabanı tablolarınızı tasarlarken gösterdiğiniz özeni, DOM hiyerarşinizi ve HTML5 semantiğinizi kurgularken göstermezseniz, dünyanın en hızlı kodunu da yazsanız arama motorları için “görünmez” olursunuz.

Bu süreçte aldığım en büyük ders şu oldu: Clean Architecture (Temiz Mimari) sadece backend’de uygulanan soyut bir kavram değildir.

  1. Single Source of Truth (Tek Doğruluk Kaynağı) prensibini meta etiketleri ve <head> yönetiminde uygulamak,
  2. Separation of Concerns (Sorumlulukların Ayrılığı) ilkesini <main>, <article> ve <header> etiketlerinin semantiğinde yaşatmak,
  3. Ve Data Flow (Veri Akışı) mantığını MDX dosyalarından Layout’a kadar kesintisiz ve prop tabanlı bir zincirle kurmak…

Bunların hepsi birer pazarlama taktiği değil, saf Teknik SEO ve Yazılım Mimarisi konularıdır.

Lighthouse panelindeki o yeşil 100 puanları görüp kutlama yapmadan önce; DOM ağacınızı, Canonical URL’lerinizi ve Schema veri akışınızı bir Tech Lead gözüyle test edin.

Unutmayın: Kötü mimari eninde sonunda çöker. Bazen sunucuda “Panic” vererek, bazen de Google Search Console’da sessizce kaybolarak. Kodunuz temiz, mimariniz sağlam, index’leriniz bol olsun.