---
title: 'Ağ Başarımı'
source: 'https://academia.sh/tr/kurslar/frontend-kalitesi/ag-basarimi'
course: 'Frontend Kalitesi'
language: tr
updated: '2026-08-17T18:09:22+00:00'
license: 'CC BY-SA 4.0'
---

# Ağ Başarımı

Tazelik ile doğrulamanın ayrılması, önbellek başlıklarının yerel bir sunucu üzerinde ölçülmesi, içerik karmalı adların değişmezlik sözü ve bağlantı ipuçlarının neyi kazandırdığı.

Önceki üç ders belgenin ve varlıkların kendisini ele aldı: hangi dosya, ne kadar büyük, ne
zaman istenir. Bir katman daha var ve orada tek bir bayt bile azaltmadan kazanç elde
edilebilir. Aynı kullanıcı istasyon listesini ikinci kez açtığında aynı dosyaları yeniden
indirmek zorunda mıdır?

Bu sorunun yanıtı istekte değil, yanıtın taşıdığı başlıklardadır. Tarayıcı ve Web Platformu
kursundaki Önbellek Stratejileri dersi program denetimli önbelleği ele almıştı; buradaki
konu, program yazılmadan sunucunun başlıklarla neyi belirlediğidir.

## Tazelik ve Doğrulama İki Ayrı Karardır

HTTP önbelleği iki ayrı soruyu yanıtlar ve bunları karıştırmak yanlış yapılandırmanın en
yaygın nedenidir.

**Tazelik ömrü** şu soruyu yanıtlar: bu yanıt ne kadar süre sorgusuz kullanılabilir?
`Cache-Control` başlığındaki `max-age` yönergesi bir süre verir; süre dolana kadar tarayıcı
**hiç istek yapmaz**. En ucuz istek, yapılmayan istektir.

**Doğrulayıcı** şu soruyu yanıtlar: elimdeki kopya hâlâ geçerli mi? `ETag` başlığı içeriğe
bağlı bir imza taşır. Tazelik süresi dolduğunda tarayıcı bu imzayı `If-None-Match` başlığıyla
geri gönderir; içerik değişmemişse sunucu gövdesiz bir yanıt döndürür.

İki karar birbirini tamamlar. Tazelik isteği tümüyle önler, doğrulama isteği ucuzlatır.
`no-cache` yönergesi bir yanlış anlamaya yol açar: saklamayı yasaklamaz, yalnızca her
kullanımdan önce doğrulamayı zorunlu kılar. Saklamayı yasaklayan yönerge `no-store`
yönergesidir.

## Başlıkları Ölçmek

Aşağıdaki sunucu üç kaynağı üç ayrı politikayla sunar ve koşullu isteği gerçekten
karşılar.

```js
// onbellek-sunucusu.mjs — tazelik ve dogrulama basliklarini uc ayri kaynakta gosterir
import { createServer } from "node:http";
import { createHash } from "node:crypto";

const govdeler = {
  "/varlik/istasyon.9f2c1a.css": {
    tur: "text/css",
    icerik: ".istasyon-listesi{display:grid;gap:12px}",
    // Icerik karmali ad: adres degisirse icerik degismistir.
    onbellek: "public, max-age=31536000, immutable",
  },
  "/api/istasyonlar": {
    tur: "application/json",
    icerik: JSON.stringify({ istasyonlar: ["kuzey-yamac-1", "kuzey-yamac-2"] }),
    // Her kullanimda dogrulanir; govde yalnizca degistiyse gonderilir.
    onbellek: "no-cache",
  },
  "/api/duyuru": {
    tur: "application/json",
    icerik: JSON.stringify({ duyuru: "Bakim penceresi: 03:00-04:00" }),
    // Bir dakika taze; sonraki bes dakika eskisi sunulup arkada tazelenir.
    onbellek: "public, max-age=60, stale-while-revalidate=300",
  },
};

const etiket = (icerik) =>
  `"${createHash("sha256").update(icerik).digest("hex").slice(0, 16)}"`;

createServer((istek, yanit) => {
  yanit.sendDate = false;
  const kaynak = govdeler[istek.url];
  if (!kaynak) {
    yanit.writeHead(404, { "Content-Type": "text/plain" });
    return yanit.end("yok\n");
  }

  const dogrulayici = etiket(kaynak.icerik);
  const basliklar = {
    "Cache-Control": kaynak.onbellek,
    ETag: dogrulayici,
    Vary: "Accept-Encoding",
  };

  // Kosullu istek: istemcinin elindeki dogrulayici hala gecerliyse govde gonderilmez.
  if (istek.headers["if-none-match"] === dogrulayici) {
    yanit.writeHead(304, basliklar);
    return yanit.end();
  }

  yanit.writeHead(200, {
    ...basliklar,
    "Content-Type": kaynak.tur,
    "Content-Length": Buffer.byteLength(kaynak.icerik),
  });
  yanit.end(kaynak.icerik);
}).listen(8241, "127.0.0.1", () => console.log("dinleniyor: 127.0.0.1:8241"));
```

```bash
#!/usr/bin/env bash
# onbellek-sunucusu.mjs'yi baslatir, tazelik ve dogrulama davranisini olcer, durdurur.
node onbellek-sunucusu.mjs > /dev/null &
sunucu=$!
sleep 1
temel=http://127.0.0.1:8241

for yol in /varlik/istasyon.9f2c1a.css /api/istasyonlar /api/duyuru; do
  printf '%s\n' "$yol"
  curl -sS -o /dev/null -D - "$temel$yol" \
    | grep -iE '^(HTTP/|cache-control|etag|content-length)' \
    | tr -d '\r' | sed 's/^/  /'
done

printf '\n--- kosullu istek ---\n'
etiket=$(curl -sS -o /dev/null -D - "$temel/api/istasyonlar" \
  | awk 'tolower($1) == "etag:" { print $2 }' | tr -d '\r')
printf 'istemcideki dogrulayici: %s\n' "$etiket"

curl -sS -o /dev/null -H "If-None-Match: $etiket" "$temel/api/istasyonlar" \
  -w 'kosullu istek  -> durum %{http_code}, indirilen govde %{size_download} bayt\n'
curl -sS -o /dev/null "$temel/api/istasyonlar" \
  -w 'kosulsuz istek -> durum %{http_code}, indirilen govde %{size_download} bayt\n'

kill "$sunucu"
```

```
/varlik/istasyon.9f2c1a.css
  HTTP/1.1 200 OK
  Cache-Control: public, max-age=31536000, immutable
  ETag: "5102555c20f3670b"
  Content-Length: 40
/api/istasyonlar
  HTTP/1.1 200 OK
  Cache-Control: no-cache
  ETag: "c0c1001d3d358ea1"
  Content-Length: 49
/api/duyuru
  HTTP/1.1 200 OK
  Cache-Control: public, max-age=60, stale-while-revalidate=300
  ETag: "caefa267f7a6d1f0"
  Content-Length: 41

--- kosullu istek ---
istemcideki dogrulayici: "c0c1001d3d358ea1"
kosullu istek  -> durum 304, indirilen govde 0 bayt
kosulsuz istek -> durum 200, indirilen govde 49 bayt
```

Doğrulayıcı değerleri içeriğin özetinden üretildiği için bu çıktıda sabittir; içerik
değiştiğinde değişir. Bağlantı noktası olarak seçilen 8241 keyfîdir ve boş olmalıdır.

Ölçülen fark açıktır: koşullu istek gövdesiz döner. Gidiş-dönüş yine yapılmıştır — koşullu
istek gecikmeyi ortadan kaldırmaz, yalnızca aktarımı ortadan kaldırır. Tazelik ömrü ise
gidiş-dönüşü de ortadan kaldırır. İkisi arasındaki bu fark, hangi kaynağa hangi politikanın
yazılacağını belirler.

## Değişmez Varlık, Değişken Belge

`immutable` yönergesi bir söz verir: bu adresteki içerik hiç değişmeyecek. Söz ancak
adres içerikten türetiliyorsa tutulabilir. Oluşturma Stratejileri ve Altyapı kursundaki
önbellek kırma yaklaşımı tam olarak bunu sağlar: dosya adına içerik karması yazıldığında,
içerik değiştiğinde adres de değişir ve eski adresin sonsuza kadar taze kalması bir sorun
yaratmaz.

Belge bu sözü veremez, çünkü adresi sabittir ve içeriği her yayında değişir. Belgeye
uygulanan doğru politika, uzun tazelik değil doğrulamadır.

Aradaki üçüncü durum için `stale-while-revalidate` yönergesi vardır: tazelik süresi
dolduktan sonra belirtilen süre boyunca eski kopya hemen sunulur ve tazeleme arka planda
yapılır. Kullanıcı beklemez, veri bir tur gecikmeyle tazelenir. Duyuru şeridi gibi
gecikmeye toleranslı içerik için uygundur; hesap bakiyesi gibi içerik için değildir.

Bu üç politikanın tekrar ziyarette ne kadar bayt aktardığı hesaplanabilir.

```js
// tekrar-ziyaret.mjs — baslik politikasinin tekrar ziyarette aktarilan bayta etkisi

const BASLIK_YUKU = 320; // bayt: istek + yanit basliklarinin yaklasik payi

// Istasyon listesi sayfasinin kaynaklari ve secilen politika.
const kaynaklar = [
  { ad: "belge",         bayt:  18_400, politika: "no-cache" },
  { ad: "bicem",         bayt:  62_000, politika: "immutable" },
  { ad: "giris betigi",  bayt: 148_112, politika: "immutable" },
  { ad: "ortak betik",   bayt: 101_760, politika: "immutable" },
  { ad: "yazi tipi",     bayt:  41_600, politika: "immutable" },
  { ad: "kart gorseli",  bayt:  86_400, politika: "immutable" },
  { ad: "istasyon veri", bayt:   9_240, politika: "max-age=60" },
];

const tamBoyut = (k) => BASLIK_YUKU + k.bayt;
const ilkZiyaret = kaynaklar.reduce((t, k) => t + tamBoyut(k), 0);

// Tekrar ziyarette bir kaynak icin aktarilan bayt.
function tekrarBayti(kaynak, gecenSaniye, degisenler) {
  const degisti = degisenler.has(kaynak.ad);
  switch (kaynak.politika) {
    case "immutable":
      // Icerik karmali ad: degistiyse adres degismistir, yeni kaynak bastan iner.
      return degisti ? tamBoyut(kaynak) : 0;
    case "no-cache":
      // Her kullanimda dogrulanir: degismediyse yalniz basliklar gelir.
      return degisti ? tamBoyut(kaynak) : BASLIK_YUKU;
    case "max-age=60":
      if (gecenSaniye <= 60) return 0;               // hala taze: istek yok
      return degisti ? tamBoyut(kaynak) : BASLIK_YUKU;
    default:
      return tamBoyut(kaynak);
  }
}

const senaryolar = [
  { ad: "30 saniye sonra, surum ayni", gecen: 30, degisenler: new Set() },
  { ad: "300 saniye sonra, surum ayni", gecen: 300, degisenler: new Set() },
  {
    ad: "yeni surum yayinlandi",
    gecen: 300,
    degisenler: new Set(["belge", "bicem", "giris betigi"]),
  },
];

console.log(`ilk ziyaret: ${(ilkZiyaret / 1024).toFixed(1)} KB`);
for (const s of senaryolar) {
  console.log(`\n--- ${s.ad} ---`);
  console.log("kaynak".padEnd(14) + "tekrar".padStart(10) + "  politika");
  let toplam = 0;
  for (const k of kaynaklar) {
    const b = tekrarBayti(k, s.gecen, s.degisenler);
    toplam += b;
    console.log(k.ad.padEnd(14) + `${(b / 1024).toFixed(2)} KB`.padStart(10) + "  " + k.politika);
  }
  console.log("-".repeat(36));
  console.log("TOPLAM".padEnd(14) + `${(toplam / 1024).toFixed(2)} KB`.padStart(10));
  console.log(`ilk ziyarete oran: ${(toplam / ilkZiyaret).toFixed(4)}`);
}

console.log("\nhicbir tazelik basligi yazilmasaydi her tekrar ziyaret oran 1.0000 olurdu.");
```

```
ilk ziyaret: 458.7 KB

--- 30 saniye sonra, surum ayni ---
kaynak            tekrar  politika
belge            0.31 KB  no-cache
bicem            0.00 KB  immutable
giris betigi     0.00 KB  immutable
ortak betik      0.00 KB  immutable
yazi tipi        0.00 KB  immutable
kart gorseli     0.00 KB  immutable
istasyon veri    0.00 KB  max-age=60
------------------------------------
TOPLAM           0.31 KB
ilk ziyarete oran: 0.0007

--- 300 saniye sonra, surum ayni ---
kaynak            tekrar  politika
belge            0.31 KB  no-cache
bicem            0.00 KB  immutable
giris betigi     0.00 KB  immutable
ortak betik      0.00 KB  immutable
yazi tipi        0.00 KB  immutable
kart gorseli     0.00 KB  immutable
istasyon veri    0.31 KB  max-age=60
------------------------------------
TOPLAM           0.63 KB
ilk ziyarete oran: 0.0014

--- yeni surum yayinlandi ---
kaynak            tekrar  politika
belge           18.28 KB  no-cache
bicem           60.86 KB  immutable
giris betigi   144.95 KB  immutable
ortak betik      0.00 KB  immutable
yazi tipi        0.00 KB  immutable
kart gorseli     0.00 KB  immutable
istasyon veri    0.31 KB  max-age=60
------------------------------------
TOPLAM         224.41 KB
ilk ziyarete oran: 0.4892

hicbir tazelik basligi yazilmasaydi her tekrar ziyaret oran 1.0000 olurdu.
```

Kaynak boyutları hesabın girdisidir, ölçülmüş bir uygulamanın çıktısı değildir; başlık payı
da bir varsayımdır. Hesaplanan şey politikanın sonucudur.

İlk iki senaryoda tekrar ziyaret neredeyse bedavadır. Üçüncü senaryo asıl dersi verir:
yeni sürüm yayınlandığında yalnızca **değişen** parçalar yeniden iner. Ortak betik
değişmediği için adresi de değişmemiştir ve hiç istenmez. Paketleme kararlarının başarım
sonucu buradadır — sık değişen kodla seyrek değişen kod aynı parçada toplanırsa, her yayın
ikisini birden yeniden indirtir.

## Vary ve Önbellek Anahtarı

Bir yanıt istek başlıklarına göre değişiyorsa bu, `Vary` başlığıyla bildirilmelidir.
Bildirilmezse ara önbellekler tek bir kopyayı herkese sunar: sıkıştırılmış bir yanıt
sıkıştırma desteklemeyen bir istemciye, bir dile göre üretilmiş sayfa başka dildeki
kullanıcıya gidebilir.

Kural, doğruluk kuralıdır ve başarımı da etkiler: `Vary` listesine gereksiz başlık
eklemek, önbellek anahtarını gereğinden fazla böler ve isabet oranını düşürür. Kullanıcıya
özgü bir başlık listeye girerse önbellek pratikte devre dışı kalır.

## Bağlantı İpuçları

Belgede yazılan bağlantı ipuçları, tarayıcıya bir işi erken yapmasını bildirir. Her biri
farklı bir aşamayı öne çeker.

`dns-prefetch` yalnızca ad çözümlemesini yapar. En ucuz ipucudur ve en az kazandırır.

`preconnect` ad çözümlemesini, taşıma bağlantısını ve güvenli el sıkışmayı önden yapar.
Kritik yol modelinde "bağlantı kurulumu" olarak sayılan turları, kaynak istenmeden önce
harcar. Bedeli vardır: kurulan ve kullanılmayan bağlantı hem istemcide hem sunucuda
kaynak tutar, bu yüzden yalnızca kesin kullanılacak kökenler için yazılır.

`preload` belirli bir kaynağı erken ister. Kritik Oluşturma Yolu dersinde anlatıldığı gibi
bu bir düzeltmedir: geç keşfedilen kritik kaynağı öne çeker. `as` özniteliği zorunludur;
yanlış yazıldığında kaynak iki kez indirilir.

`prefetch` bir **sonraki** gezinmede gerekecek kaynağı boşta indirir. Öncelik en düşüktür
ve kullanıcı o sayfaya gitmezse harcanmış bant genişliğidir.

Sıra bir öncelik sırasıdır: önce zinciri düzleştirmek, sonra `preconnect`, en son
`preload`. Ölçüm göstermeden yazılan ipucu, çoğu zaman kritik kaynağın bant genişliğini
alarak zarar verir.

## Özet

- Tazelik ömrü isteği tümüyle önler, doğrulayıcı isteği gövdesiz hâle getirir; ikisi ayrı
  sorunları çözer ve `no-cache` saklamayı değil doğrulamasız kullanımı yasaklar.
- İçerik karmalı adres, `immutable` sözünün tutulabilmesinin koşuludur; sabit adresli belge
  bu sözü veremez ve doğrulamayla yönetilir.
- Yeni bir yayında yalnızca adresi değişen parçalar yeniden iner; paketleme kararı bu
  yüzden bir önbellek kararıdır.
- `Vary` başlığı önce doğruluk, sonra başarım konusudur: eksikliği yanlış yanıt sunar,
  fazlalığı önbelleği böler.
- Bağlantı ipuçları farklı aşamaları öne çeker ve hepsinin bir bedeli vardır; ölçüm
  göstermeden yazılan ipucu kritik kaynağın önünü keser.

## Sonraki Adım

Ağ tarafı düzenlendiğinde baytlar zamanında gelir ve tekrar ziyaret neredeyse istek
yapmaz. Buna karşın uygulama hâlâ ağır hissettirebilir: liste kaydırılırken takılıyor,
süzgeç kutusuna yazılan harf ekranda geç görünüyor, tarih aralığı değiştirildiğinde sayfa
bir an donuyor olabilir. Bu sorunların hiçbiri ağda değildir; hepsi indirilmiş kodun tek
bir sırada çalışmasından doğar. Sonraki ders bu sırayı ele alır: uzun görevlerin nasıl
ölçüldüğünü, bölüntülemenin ne kazandırdığını ve yerleşim sarsıntısının satır sayısıyla
nasıl ölçeklendiğini hesaplar.
