İçeriğe Atla
Mustafa Erbay
Teknoloji İnsan tarafından yazıldı Production Diaries · 8 dk okuma · görüntülenme Read in English
100%

Cloudflare HTML Cache %1.1'de Takılı: Nginx map ile Kurtarma

Cloudflare cache %1.1'de takılıydı. Astro Node adapter HTML için max-age=0 dönüyor. nginx map directive ile content-type bazlı override.

Cloudflare HTML Cache %1.1'de Takılı: Nginx map ile Kurtarma — yaşanmış hikaye kapak görseli

Cloudflare dashboard %1.1 cache rate gösteriyordu

Bu hafta düzenli olarak performance dashboard’a bakma alışkanlığı edindim. mustafaerbay.com.tr Cloudflare zone’unun istatistiklerini açtım. Total Requests 4.1k, Unique Visitors 1.16k. Güzel, oluyor. Sonra Percent Cached satırı:

Percent Cached: 1.11%

Yüzde bir virgül bir bir. Yani 4.1k request’in 4.05k tanesi her seferinde origin’e (VPS’ime) gidiyor. Cloudflare neredeyse hiçbir şey cache’lemiyor.

Bu sayı yanlış olsa neyse, gerçek. Bana bir kaç saniye lagsızlık ve VPS RAM’imi yiyor. Sebebini bulmam lazım.

İlk hipotez: Cloudflare yanlış configured

Belki cache rule ayarım yoktu. Dashboard’da Caching → Configuration → “Caching Level: Standard” diyordu. Standard demek “default’a göre cache et”. Default’ta HTML cache değil. Hmm. Ama nedeni “Caching Level” değil — daha temel bir sorun olmalı.

Origin’den ne header döndürdüğümü kontrol ettim:

$ curl -sI https://mustafaerbay.com.tr/blog/technology/some-post/ | grep -iE "cache|expires|content-type|cf-cache"
content-type: text/html; charset=utf-8
cache-control: public, max-age=0
last-modified: Tue, 28 Apr 2026 10:38:17 GMT
cf-cache-status: DYNAMIC

İşte. cache-control: public, max-age=0.

Cloudflare bu header’ı görünce “browser bile bunu cache etme dedi, ben de etmem” diyor → DYNAMIC. Edge cache 0%. Origin yapıyor.

Hashed asset’lere bakayım:

$ curl -sI https://mustafaerbay.com.tr/_astro/ClientRouter...js | grep cache
cache-control: public, max-age=31536000, immutable
cf-cache-status: HIT

JS’ler perfect — 1 yıllık immutable, Cloudflare HIT. Yani Astro hashed asset için immutable header’ı koyuyor. Sadece HTML’de max-age=0 koyuyor — büyük olasılıkla SSR rotaları için defensive default.

Astro Node adapter’ı suçlu

Astro’nun Node adapter’ı SSR rotaları için “her response taze” yaklaşımı kullanıyor. Ama benim sayfalarımın çoğu prerendered static HTML. Sadece /api/* ve birkaç dynamic sayfa SSR. Geri kalan static dosyalar.

Doğru davranış: HTML için cache header’ını manuel olarak override etmek. Astro Node adapter response’una direkt müdahale etmek karmaşık. Daha temiz: nginx layer’ında override.

nginx map directive ile content-type bazlı override

Nginx’te şu pattern çok güçlüdür:

map $upstream_http_content_type $mb_cache_control {
    default                  $upstream_http_cache_control;
    "~*^text/html"           "public, max-age=300, s-maxage=3600, stale-while-revalidate=86400";
    "~*^application/xml"     "public, max-age=900, s-maxage=3600";
    "~*^application/rss\+xml" "public, max-age=900, s-maxage=3600";
}

server {
    # ... ssl, server_name vs ...

    location / {
        proxy_pass http://127.0.0.1:3040;
        # ...

        # Override Cache-Control for HTML/feeds; passthrough for assets and /api/*.
        proxy_hide_header Cache-Control;
        add_header        Cache-Control $mb_cache_control always;
    }
}

Mantık:

  1. Upstream (Astro Node) response’unu yakalar
  2. Content-Type header’ına bakıyorum
  3. Eğer text/html ise → s-maxage=3600 (CDN 1 saat cache)
  4. Eğer application/xml ise → 15 dk
  5. Geri kalan (JS, CSS, image, JSON) → upstream’in kendi header’ını passthrough eder

proxy_hide_header upstream’in Cache-Control’unu siler. Sonra add_header benim map’lediğimi koyar. Trafik akışı:

Browser → Cloudflare (s-maxage'i okur, 1 saat cache) → nginx (override) → Astro

Bir tuzak: add_header always

Önce always flag’i olmadan kurmuştum. 4xx ve 5xx response’larda Cache-Control header gelmedi. /api/views için 404 dönerken cache-control: no-store gerekiyor (Astro upstream’i bunu koyuyor) ama nginx hide etmiş, kendi map’i 404 için davranmıyor → boş.

always ile çözülüyor:

add_header Cache-Control $mb_cache_control always;

Şimdi tüm status code’larda map devreye giriyor. application/json (API) → upstream value passthrough → no-store döner. text/html (404 sayfası) → max-age=300 → istemci 5 dakika cache. Bu da OK çünkü 404’lar fazla değişmiyor.

Doğrulama

$ curl -sI https://mustafaerbay.com.tr/blog/technology/some-post/ | grep cache
content-type: text/html; charset=utf-8
cache-control: public, max-age=300, s-maxage=3600, stale-while-revalidate=86400
cf-cache-status: HIT

Üç parametre:

  • max-age=300 — browser 5 dk cache (yeni içeriği görmek için F5)
  • s-maxage=3600 — Cloudflare 1 saat cache (CDN)
  • stale-while-revalidate=86400 — Origin yavaşsa stale serve et + arka planda yenile

Bunu deploy ettikten sonra Cloudflare dashboard’a 24 saat verdim:

Percent Cached: 47.3%

%1.1 → %47.3. 47 katı. Origin’ime giden istek yarıdan az. VPS RAM’imin nefes alması, sayfa yüklenme süresinin yarıya inmesi, Türkiye dışı istekler için Cloudflare edge’inden hızlı response.

Daha geniş ders

Cache’i config etmek defaults’a güvenmemekle başlar. Astro Node adapter “her sayfa SSR olabilir” varsayımıyla max-age=0 koyuyor. Bu bir defensive default, anlaşılabilir. Ama benim sitem %95 prerendered, Astro’nun bilmediği bir şey. Override edip “ben biliyorum bu sayfa 1 saat değişmez” demek benim sorumluluğum.

İkinci ders — performance ölçümü vibe’la değil gözle metrikle yapılır. Percent Cached istatistiğini izlememek beni “site hızlı görünüyor” dünyasında tutardı. Dashboard’a bakmak, sayıyı görmek, “yüzde 1 ne için” diye sormak — bu refleks performance optimizasyonunun başlangıç noktası.

Yarın aynı dashboard’a tekrar bakacağım. 47’den yukarı gidecek mi yoksa o civarda asimptot çekecek mi göreceğim. Hangi bölümün cache’lenmediğine derin dalmak gerekirse de top 10 MISS path’lere bakacağım. Ama şu an: 47x improvement. İyi gün.

Paylaş:

Bu yazı faydalı oldu mu?

Yükleniyor...

Bu yazı nasıldı?

ME

Mustafa Erbay

Sistem Mimarisi · Network Uzmanı · Altyapı, Güvenlik ve Yazılım

2006'dan bu yana sistem mimarisi, network, sunucu altyapıları, büyük yapıların kurulumu, yazılım ve sistem güvenliği ekseninde çalışıyorum. Bu blogda sahada karşılığı olan teknik deneyimlerimi paylaşıyorum.

Kişisel Notlar

Bu notlar sadece sizde saklanır. Tarayıcınızda yerel olarak tutulur.

Hazır 0 karakter

Yorumlar

Sunucu Taraflı AI Moderasyon

Yorumlar sunucuda yapay zeka ile denetlenir ve kalıcı olarak saklanır.

?
0/2000

Sunucu taraflı AI denetim

✉️ Ücretsiz · Spam yok · İstediğin an çık

Haftalık özet — AI değil, bizzat ben seçiyorum

Haftada bir mail: o haftanın en önemli yazısı, perde arkası notları, ve "bu hafta gerçekten kullandığım araç" bölümü. Az gürültü, çok sinyal.

  • 📌
    Haftanın en iyisi Sadece okumaya değer tek yazı
  • 🔧
    Alet çantası Bu hafta kullandığım araçlar
  • 🧠
    Perde arkası Blog'a girmeyen notlar

Spam yapmıyoruz. İstediğiniz zaman ayrılabilirsiniz. · Sadece Umami (self-hosted, Google yok) ile takip.

Okuma İstatistikleriniz

0

Yazı Okundu

0dk

Okuma Süresi

0

Gün Serisi

-

Favori Kategori

İlgili Yazılar