Render Süreci / render blocking

Render Blocking Kaynaklar Nedir ve Nasıl Çözülür?

Render Blocking Kaynaklar Nedir ve Nasıl Çözülür? için performans görseli

Render-blocking kaynaklar ilk ekranı geciktirdiğinde kullanıcı sayfa yükleniyor mu, takıldı mı ayrımını yapamaz hale gelir.

Sorun çoğunlukla kaçınılmaz gibi görünür, oysa çoğu bloklayan kaynak yanlış önceliklendirmeden kaynaklanır. Hangi dosyanın gerçekten kritik yolda olduğunu belirlemek, hangi dosyanın sadece yanlış sıralanmış olduğunu ayırt etmekle başlar.

İlk boya süresi düştüğünde layout kırılması üretmediyseniz gerçek bir kazanım elde ettiniz demektir.

Tarayıcı sayfayı nasıl çizer? Render blocking'in temeli

Render blocking kavramını anlamak için önce tarayıcının bir sayfayı nasıl görüntülediğini bilmek gerekir. Tarayıcı HTML'yi indirip ayrıştırmaya başladığında DOM (Document Object Model) ağacını oluşturur. Aynı anda CSS dosyaları da ayrıştırılarak CSSOM (CSS Object Model) ağacı oluşturulur. Bu iki ağaç birleştirilerek Render Tree kurulur ve ekrana ilk piksel çizilir.

Kritik olan nokta şudur: Render Tree tamamlanmadan tarayıcı ekrana hiçbir şey çizmez. Render Tree'nin tamamlanması için hem DOM hem de CSSOM'un hazır olması gerekir. Dolayısıyla CSS dosyası indirilip ayrıştırılmadan sayfanın ilk boyaması gerçekleşemez. Bu yüzden head bölümüne eklenen her CSS dosyası potansiyel bir bloklayıcıdır.

JavaScript da bu denkleme dahildir. Tarayıcı bir script etiketiyle karşılaştığında —eğer defer veya async özelliği yoksa— HTML ayrıştırmayı duraklatır, scripti indirir, çalıştırır ve ancak ondan sonra DOM oluşturmaya devam eder. Bu duraklatma ne kadar uzun sürerse kullanıcı o kadar geç içerik görür.

CSS render blocking: hangi senaryolar sorun yaratır?

Her CSS dosyası render blocking değildir. Tarayıcı, media attribute'u olan CSS dosyalarını değerlendirerek yalnızca mevcut görünüm koşullarıyla eşleşen stilleri kritik kaynak olarak işler. Örneğin media="print" ile yüklenen bir CSS dosyası ekranda görüntülenirken bloklayıcı sayılmaz; ancak indirilmesi yine de gerçekleşir.

Sorun genellikle şu senaryolarda ortaya çıkar: tek bir büyük CSS dosyasına tüm stillerin doldurulması, sayfa ilk yüklenişinde kullanılmayacak bileşen stillerinin de bu dosyada yer alması ve CDN yerine yavaş bir kaynaktan servis edilmesi. Kullanıcı henüz o bileşeni görmemişken tarayıcı o bileşenin stilini indirmek için beklemek zorunda kalır.

Çözüm yaklaşımları şu sıraya göre uygulanabilir: ilk ekran için zorunlu olan stiller inline olarak head'e alınır (critical CSS), geri kalan stiller sayfa yüklendikten sonra asenkron olarak eklenir. Bu yaklaşım critical CSS konusunda daha ayrıntılı ele alınmıştır. Ek olarak, kullanılmayan CSS kurallarını temizlemek (CSS purging) toplam dosya boyutunu küçültür ve ayrıştırma süresini azaltır.

  • Media attribute kullanımı: Ekran dışı stiller için media="print" veya kırılım noktasına özgü değerler ekleyin, critical path yükünü azaltın.
  • CSS purging: PurgeCSS veya benzeri araçlarla kullanılmayan kuralları üretim build'inden çıkarın, dosya boyutunu %40-70 küçültebilirsiniz.
  • Async CSS yükleme: Kritik olmayan CSS'i rel="preload" ve onload callback kombinasyonuyla asenkron yükleyin.

Font yüklemesi neden render blocking olabilir?

Web fontları render blocking kaynakların gözden kaçan bir kategorisidir. Tarayıcı bir text elemanını çizmek için önce hangi fontun kullanılacağını belirler, sonra o fontu indirir ve ancak o zaman metni görünür kılar. Bu süreç yanlış yapılandırıldığında kullanıcı metin alanında boşluk görür; bu duruma FOIT (Flash of Invisible Text) denir.

Google Fonts gibi harici font servisleri ek bir sorun katmanı ekler. Tarayıcı önce font sağlayıcısının CSS dosyasını indirir, ardından font URL'lerini öğrenir ve asıl font dosyalarını indirir. Bu zincirleme istek yapısı özellikle mobil ağlarda belirgin gecikmelere yol açar.

font-display: swap bu sorunun en hızlı çözümüdür. Bu değer tarayıcıya "font hazır değilken sistem fontuyla göster, hazır olunca değiştir" talimatı verir. Kullanıcı metni anında görmüş olur; font yüklendiğinde küçük bir görsel değişim yaşanır ama boş alan görmez. Preconnect ipuçları ile font sağlayıcısına olan bağlantı önceden kurulabilir; bu adım tek başına 100-300ms kazandırabilir.

  • font-display: swap: CSS'te @font-face kuralına ekleyin, FOIT sorununu ortadan kaldırır, ilk metin görünümü anında gerçekleşir.
  • Font self-hosting: Fontları kendi sunucunuzda barındırmak harici DNS çözümlemesi ve bağlantı kurma süresini ortadan kaldırır.
  • Subsetting: Fontun yalnızca kullanılan karakter setini indirin, Türkçe için Latin Extended-A yeterlidir; tüm dil desteğini indirmeye gerek yoktur.
  • Preconnect: <link rel="preconnect"> ile font sağlayıcısına erken bağlantı açın, DNS çözümleme süresini ortadan kaldırın.

Lab ve alan verisini birlikte okuyun

Gerçek kullanıcı verisi laboratuvar testinden farklı sonuç verir.

Aynı sayfa farklı cihazlarda farklı hız gösterir. Mobil tarafta düşük işlem gücü ve ağ dalgalanması masaüstünde görünmeyen gecikmeleri büyütür. Tek sayı üzerinden acele karar vermek yerine dağılıma bakın: 75. persentil, 90. persentil ve en kötü %5'lik dilim ayrı ayrı incelenmeli.

  • Gerçek kullanıcı verisi: CrUX raporunu haftalık kontrol edin, 28 günlük trend grafiğini izleyin.
  • Laboratuvar testi: Lighthouse skorunu 3G throttling ile ölçün, mobil cihaz simülasyonu açık olsun.
  • Cihaz kırılımı: Düşük, orta ve yüksek performanslı cihazları ayrı segmentlere ayırın.
  • Zaman karşılaştırması: Her değişiklik öncesi baseline kaydedin, 7 gün sonra tekrar ölçün.

Küçük disiplinler uzun vadede büyük fark yaratır. Değişiklik öncesi ve sonrası aynı ölçüm koşullarını korursanız ekip içi değerlendirmede tartışma azalır, karar kalitesi yükselir. Belirlediğiniz eşikleri sürüm notlarına işleyin; yeni geliştirmeler performans çizgisini bozduğunda daha erken yakalarsınız.

Hangi kaynak gerçekten blokluyor, hangisi değil?

Kaynak sıralamasını düzeltirken critical CSS uygulaması ve JavaScript defer async planı birlikte ele alındığında ilk çizim daha stabil olur.

Ağır medya, bloklayan kaynak, ana iş parçacığı yükü ve sunucu gecikmesi dört ayrı katmandır ve birbirinden bağımsız çalışmaz. 2MB hero görsel WebP'ye çevrilse bile lazy loading eklenmezse bloklama devam eder. Harici CSS 400ms alıyorsa inline critical CSS devreye alınabilir; 300KB bundle code splitting ile parçalanırsa Time to Interactive düşer.

Sunucu gecikmesi TTFB'yi yükseltir. CDN kullanıyorsanız cache hit oranını kontrol edin; %60'ın altındaysa cache stratejinizi gözden geçirin.

  • Ağır medya: Hero görseli 150KB altına düşürün, above-the-fold dışındaki görsellere loading="lazy" ekleyin.
  • Bloklayan kaynak: Critical CSS'i inline yapın (max 14KB), geri kalan stilleri async yükleyin.
  • Ana iş parçacığı yükü: Long Task'ları tespit edin (50ms üzeri), büyük JavaScript dosyalarını dynamic import ile bölün.
  • Sunucu gecikmesi: TTFB 600ms üzerindeyse sunucu tarafı cache ekleyin, database query'lerini optimize edin.

Bir metriği iyileştirirken başka bir alanda sorun üretmek kolay. LCP'yi düşürmek için tüm görselleri preload ederseniz bandwidth tüketimi artar, düşük bant genişliğindeki kullanıcılar daha yavaş deneyim yaşar.

Çözüm sırasını etki ve risk ekseninde belirleyin

Tüm sorunları aynı anda çözemezsiniz.

Etki büyüklüğü ile uygulama maliyetini karşılaştırın. Critical CSS inline yapmak 2 saatlik iş, LCP'yi 800ms düşürür. Font subsetting 4 saatlik iş, LCP'yi 150ms düşürür. Hangisi önce? İlki. Uygulama maliyeti düşük, etki büyük olan adımları önceliklendirin.

Risk seviyesini hesaba katın: üçüncü taraf script'i kaldırmak analytics verilerini bozabilir, geri alma planı olmadan canlıya almayın. Geri dönüş süresi kısa olan değişiklikleri tercih edin: feature flag ile kontrol edilen optimizasyonlar sorun çıkarsa anında geri alınabilir.

  • Etki büyüklüğü: Lighthouse'da "Opportunities" bölümündeki tahmini kazanımlara bakın, 500ms üzeri kazanım veren adımları önceliklendirin.
  • Uygulama maliyeti: 1 günden kısa sürecek değişiklikleri hızlı kazanım için seçin, uzun projeler için ayrı sprint planlayın.
  • Risk seviyesi: Kritik iş akışını etkileyecek değişiklikleri staging'de 1 hafta test edin, A/B test ile %10 trafiğe açın.
  • Geri dönüş süresi: Feature flag veya CDN konfigürasyonu ile kontrol edilen değişiklikleri tercih edin, kod deploy gerektiren değişiklikleri son sıraya bırakın.

Müdahaleleri kademeli ve geri alınabilir şekilde uygulayın

Büyük değişiklikler risk taşır.

Aşamalı yayın yapın: önce %5 trafiğe açın, 24 saat bekleyin, metrik stabil kalırsa %25'e çıkarın. Önce test sonra canlı: staging ortamında 3 gün çalıştırın, gerçek kullanıcı verisi simülasyonu yapın, sorun yoksa production'a alın. Regresyon kontrolü: her deploy sonrası Lighthouse skorunu otomatik çalıştırın, LCP 200ms üzeri artarsa alarm tetikleyin.

Geri alma planı hazırlayın. Feature flag kullanıyorsanız tek tıkla eski versiyona dönebilirsiniz. CDN konfigürasyonu değiştirdiyseniz önceki ayarları yedekleyin, sorun çıkarsa 5 dakikada geri alın.

  • Aşamalı yayın: Canary deployment ile %5 → %25 → %50 → %100 şeklinde ilerleyin, her aşamada 24 saat bekleyin.
  • Önce test sonra canlı: WebPageTest ile 9 farklı lokasyondan test edin, median değer baseline'dan %10'dan fazla sapma gösteriyorsa değişikliği gözden geçirin.
  • Regresyon kontrolü: CI/CD pipeline'a Lighthouse CI ekleyin, LCP budget'ı 2.5s olarak ayarlayın, aşılırsa build'i fail edin.
  • Geri alma planı: Her değişiklik için rollback prosedürü yazın, maksimum geri alma süresi 15 dakika olsun.

Lab skoru ile alan verisi ayrışırsa nedeni araştırın

Doğrulama adımında Lighthouse rapor okuma yöntemi ile TTFB katman analizi bir arada izlendiğinde hatalı çıkarım oranı düşer.

Tek ölçüm yanılgısı: Lighthouse'da 95 skor aldınız, gerçek kullanıcı verisi 65 gösteriyor. Neden?

Laboratuvar testi ideal koşullarda çalışır: sabit ağ hızı, temiz cache, tek sayfa yüklemesi. Gerçek kullanıcı farklı koşullarda gezinir: yavaş 3G, dolu cache, çoklu sekme, arka planda çalışan uygulamalar. Aşırı ortalama kullanımı gerçeği gizler: ortalama LCP 2.1s olabilir ama %25'lik dilim 4.8s görüyordur. Coğrafya farkı büyük: Avrupa'dan 1.2s yüklenen sayfa Güneydoğu Asya'dan 3.7s yüklenir. Cihaz sınıfı etkisi: iPhone 14'te 800ms olan parse süresi Android Go cihazda 2.4s'ye çıkar.

  • Tek ölçüm yanılgısı: CrUX verisi ile Lighthouse skorunu karşılaştırın, %20'den fazla fark varsa gerçek kullanıcı verisine odaklanın.
  • Aşırı ortalama kullanımı: Median yerine 75. persentil değerini hedef alın, Google Core Web Vitals de bu değeri kullanır.
  • Coğrafya farkı: Trafiğinizin %80'inin geldiği 3 lokasyonu tespit edin, her birinden ayrı ölçüm yapın.
  • Cihaz sınıfı etkisi: Düşük performanslı cihazları (2GB RAM altı) ayrı segment olarak izleyin, bu segment için ayrı optimizasyon hedefi belirleyin.

İzleme sistemi olmadan kazanım kalıcı olmaz

Tek seferlik optimizasyon yetmez.

Haftalık denetim yapın: her Pazartesi sabahı CrUX raporunu açın, LCP ve FID değerlerini önceki haftayla karşılaştırın, %10'dan fazla artış varsa nedenini araştırın. Yayın öncesi kontrol: her production deploy öncesi Lighthouse CI çalıştırın, performance budget'ı aşan değişikliği merge etmeyin. Otomatik alarm: LCP 2.5s'yi geçtiğinde Slack'e bildirim gönderin, 3.0s'yi geçtiğinde on-call developer'ı uyandırın.

Dokümantasyon disiplini: her optimizasyonu wiki'ye yazın, hangi değişiklik hangi metriği ne kadar iyileştirdi kayıt altına alın. 6 ay sonra aynı sorunu tekrar yaşadığınızda önceki çözümü 5 dakikada bulursunuz.

  • Haftalık denetim: CrUX raporunu her Pazartesi kontrol edin, trend grafiğinde ani düşüş veya yükseliş varsa root cause analysis yapın.
  • Yayın öncesi kontrol: Lighthouse CI'ı GitHub Actions'a entegre edin, LCP budget 2.5s, FID budget 100ms, CLS budget 0.1 olarak ayarlayın.
  • Otomatik alarm: Datadog veya New Relic ile real user monitoring kurun, 75. persentil LCP 3.0s'yi geçtiğinde PagerDuty ile alert gönderin.
  • Dokümantasyon disiplini: Her optimizasyon için before/after screenshot, metrik karşılaştırması ve uygulama adımlarını Confluence'a yazın.

Belirlediğiniz eşikleri sürüm notlarına işleyin; yeni geliştirmeler performans çizgisini bozduğunda daha erken yakalarsınız.

Kurumsal bir sayfada kullanıcı uzun süre boş alan görüyordu. Analizde sorun, bloklayan stil ve font zincirinin ilk boyamayı geciktirmesiydi. Ekip kritik üst alan için zorunlu stilleri ayırdı, geri kalan yükü kademelendirdi ve kaynak önceliğini düzenledi. Dosya sayısı büyük ölçüde aynı kalmasına rağmen ilk görünür içerik belirgin şekilde erkene geldi.

Kaynak sırası düzenlenmesi, dosya silmekten çok yükleme mantığının değiştirilmesiyle sonuç verir. Kritik yolu tanımlayan ve her sürümde izleyen ekipler regresyonu erkenden fark eder.