Cache API, Request ve Response nesnelerini çift olarak saklayan, tarayıcıya ait eş zamansız bir depodur. caches.open("ad") ile adlandırılmış bir önbellek açarsınız; add() ve addAll() adresleri ağdan indirip saklar, put() elinizdeki bir yanıtı saklar, match() kaydı bulur, delete() siler, keys() listeler. API service worker şartnamesinde tanımlıdır ama hem sayfada (window.caches) hem de worker'larda kullanılabilir.
Bu yazı CacheStorage ve Cache arayüzlerinin her yöntemini, eşleştirme seçeneklerini (ignoreSearch, ignoreMethod, ignoreVary), önbellekleri sürümleme ve temizleme düzenini ve sık karşılaşılan tuzakları anlatır. Service worker'ın fetch olayında bu önbellekleri hangi stratejiyle kullanacağınız çevrimdışı önbellek stratejileri yazısının konusudur. Dizinin haritası için Web API öğrenme rehberi sayfasına bakın.
Cache API ile HTTP önbelleği aynı şey mi?

Hayır. Tarayıcının HTTP önbelleği Cache-Control, ETag gibi başlıklara göre kendiliğinden çalışır; Cache API ise tamamen sizin kodunuzla yönetilen bir depodur. MDN bunu açıkça söyler: önbelleğe alma API'si HTTP önbellek başlıklarını dikkate almaz. Bir kayıt siz güncellemedikçe güncellenmez ve siz silmedikçe süresi dolmaz. max-age gibi bir değerin burada hiçbir etkisi yoktur. HTTP önbellek başlıklarını Cache-Control ve ETag yazısında, önbellek kavramını genel olarak önbellek nedir yazısında bulabilirsiniz.
| Konu | HTTP önbelleği | Cache API |
|---|---|---|
| Kim yönetir | Tarayıcı, yanıt başlıklarına göre | Sizin JavaScript kodunuz |
| Süre dolması | Cache-Control ve benzeri başlıklarla | Yok; siz silene kadar kalır |
| Erişim | Doğrudan erişilemez | caches nesnesi ile okunur ve yazılır |
| Tipik kullanım | Statik dosyaların tekrar indirilmemesi | Çevrimdışı çalışma, uygulama kabuğu, özel yanıtlar |
CacheStorage: önbellek dizini
CacheStorage, bir kökenin tüm adlandırılmış önbelleklerinin ana dizinidir ve string adları Cache nesnelerine eşler. Sayfada window.caches, worker'da WorkerGlobalScope.caches ile erişilir. Beş yöntemi vardır:
open(cacheName): Adı verilen önbelleğe çözülür; yoksa oluşturur.has(cacheName): O adla bir önbellek varsatrue, yoksafalseverir.delete(cacheName): Önbelleği bulup silersetrue, bulamazsafalseverir.keys(): Tüm önbellek adlarını içeren bir diziye çözülür.match(request, options): İsteği tüm önbelleklerde arar; bulursa yanıtı, bulamazsaundefinedverir.cacheNameseçeneğiyle aramayı tek bir önbellekle sınırlayabilirsiniz.
MDN, CacheStorage.match() yönteminin bir kolaylık olduğunu belirtir: aynı sonucu keys() ile adları alıp her önbelleği open() ile açarak ve Cache.match() çağırarak da elde edebilirsiniz.
Cache arayüzünün yöntemleri
add() ve addAll()
add(request) bir URL veya Request alır, kaynağı indirir ve yanıtı saklar. MDN'e göre işlevsel olarak "fetch() yap, yanıt başarılı değilse hata fırlat, başarılıysa put() ile kaydet" akışına eşittir. addAll(requests) aynı işi bir dizi için yapar ve kurulum sırasında uygulama kabuğunu önbelleğe almanın en yaygın yoludur. Göreli URL'ler, sayfada belgenin baseURI değerine, worker'da ise worker'ın konumuna göre çözülür.
async function kabuguOnbellegeAl() {
const cache = await caches.open("kabuk-v1");
await cache.addAll([
"/",
"/index.html",
"/style.css",
"/app.js",
]);
}Bu iki yöntem şu durumlarda TypeError ile reddedilir: URL şeması http veya https değilse ve yanıt durumu 200 aralığında değilse. İkinci kural, sunucunun 404 döndürdüğü durumu kapsadığı gibi farklı kökenden no-cors modunda yapılan isteği de kapsar, çünkü böyle opak bir yanıtın durum kodu her zaman 0 olarak görünür. addAll() listedeki tek bir başarısız adres yüzünden tamamen başarısız olur; ayrıca aynı çağrı içinde birbirinin üzerine yazacak iki kayıt varsa da başarısız olur.
put()
put(request, response) elinizdeki bir istek/yanıt çiftini saklar ve aynı isteğe ait eski kaydın üzerine yazar. add() ile farkı, durum kodunu denetlememesidir: 200 dışı yanıtları ve opak yanıtları da saklayabilir. Bu esneklik dikkat ister; hatalı bir yanıtı bilmeden saklarsanız kullanıcı sürekli o hatayı görür. MDN'in yazı tipi örneğinde olduğu gibi, saklamadan önce response.status ve gerekirse Content-Type başlığını kontrol edin.
async function agdanAlVeSakla(url) {
const yanit = await fetch(url);
if (!yanit.ok) {
throw new TypeError("bad response status");
}
const cache = await caches.open("veri-v1");
await cache.put(url, yanit.clone());
return yanit;
}Buradaki clone() çağrısı zorunludur. Yanıtın gövdesi bir akıştır ve yalnızca bir kez okunabilir; put() gövdeyi tüketir. Aynı yanıtı hem önbelleğe yazıp hem sayfaya döndürmek istiyorsanız birini kopyalamanız gerekir. Bu davranışı tarayıcıdan bağımsız olarak Node.js ile doğruladık:
const yanit = new Response(JSON.stringify({ ok: true }), {
headers: { "Content-Type": "application/json" },
});
const kopya = yanit.clone();
console.log(await yanit.text()); // {"ok":true}
console.log(yanit.bodyUsed, kopya.bodyUsed); // true false
try {
await yanit.text();
} catch (e) {
console.log(e.name); // TypeError
}
console.log((await kopya.json()).ok); // trueRequest, Response ve gövde kurallarının ayrıntısı Request, Response ve Headers nesneleri yazısındadır.
match() ve matchAll()
match(request, options) eşleşen ilk isteğin yanıtına çözülür; eşleşme yoksa hata vermez, undefined döner. Bu yüzden sonucu her zaman kontrol etmelisiniz. matchAll() eşleşen tüm yanıtların dizisini verir; istek parametresini vermezseniz önbellekteki tüm yanıtların kopyasını alırsınız.
async function onbellektenOku(url) {
const cache = await caches.open("veri-v1");
const yanit = await cache.match(url);
if (!yanit || !yanit.ok) {
return null;
}
return yanit.json();
}delete() ve keys()
delete(request, options) kaydı bulup silerse true, bulamazsa false verir. keys() önbellekteki anahtarları, yani Request nesnelerini, eklendikleri sırayla döndürür. MDN'e göre yanıtlarında Vary başlığı olan kayıtlarda aynı URL'ye sahip ama başlıkları farklı birden çok istek listelenebilir. Bir önbelleği tamamen boşaltmak için anahtarları dolaşıp silebilirsiniz:
async function onbellegiBosalt(ad) {
const cache = await caches.open(ad);
const istekler = await cache.keys();
for (const istek of istekler) {
await cache.delete(istek);
}
return istekler.length;
}Önbelleği bütünüyle kaldırmak istiyorsanız tek tek silmek yerine caches.delete(ad) çağırmak daha basittir.
Eşleştirme seçenekleri

match(), matchAll(), delete() ve keys() aynı seçenek nesnesini kabul eder. Hepsinin varsayılanı false'tur:
| Seçenek | true olduğunda |
|---|---|
| ignoreSearch | URL'nin sorgu dizesi yok sayılır; /urunler ile /urunler?sayfa=2 eşleşir |
| ignoreMethod | İsteğin HTTP metodu denetlenmez; normalde yalnızca GET ve HEAD eşleşir |
| ignoreVary | Yanıttaki Vary başlığına bakılmaz; URL eşleşmesi yeterli olur |
| cacheName | Yalnızca CacheStorage.match() için anlamlıdır; aramayı tek önbellekle sınırlar |
ignoreSearch en sık kullanılan seçenektir. MDN'in matchAll örneği, { ignoreSearch: true } ile hem / hem de /?value=bar kaydını bulur. Sorgu dizesinin URL'nin hangi parçası olduğunu tarayıcıdan bağımsız Request ve URL nesneleriyle görebilirsiniz; aşağıdaki kod Node.js ile çalıştırıldı:
const istek = new Request("https://example.com/api/urunler?sayfa=2");
console.log(istek.method); // GET
console.log(istek.url); // https://example.com/api/urunler?sayfa=2
const u = new URL(istek.url);
console.log(u.pathname); // /api/urunler
console.log(u.search); // ?sayfa=2
const post = new Request("https://example.com/api/urunler", { method: "POST", body: "x" });
console.log(post.method); // POSTİkinci istek bir POST'tur; varsayılan eşleştirme yalnızca GET ve HEAD istekleriyle çalıştığı için böyle bir istekle match() çağrısı kayıt bulmaz. ignoreMethod bu denetimi kaldırır. ignoreVary ise, örneğin Vary: Accept-Language taşıyan bir yanıtı farklı dil başlığıyla gelen istekte de kullanmak istediğinizde işe yarar; MDN'e göre anahtar eşleştirme algoritması değerdeki Vary başlığına bağlıdır.
Önbellekleri sürümlemek ve eskileri silmek
Önbellek kayıtları kendiliğinden yenilenmediği için MDN, önbellekleri adla sürümlemeyi ve her betik sürümünün yalnızca kendi güvenle işleyebildiği önbellekleri kullanmasını önerir. Tipik düzen şudur: yeni sürüm kabuk-v2 adıyla yeni bir önbellek doldurur, service worker'ın activate olayında ise izin listesinde olmayan tüm eski önbellekler silinir. Service worker yaşam döngüsünü service worker kaydı ve yaşam döngüsü yazısında anlatıyoruz.
const GECERLI = ["kabuk-v2", "veri-v2"];
self.addEventListener("activate", (event) => {
event.waitUntil(
caches.keys().then((adlar) =>
Promise.all(
adlar.map((ad) => {
if (!GECERLI.includes(ad)) {
return caches.delete(ad);
}
return undefined;
}),
),
),
);
});Service worker kullanmayan bir sayfada da aynı fikri uygulayabilirsiniz. MDN'in CacheStorage sayfasındaki örnek, myapp- önekiyle başlayan ama güncel olmayan önbellekleri silerek kullanıcının disk alanına saygı gösterir. Önek kontrolü önemlidir: aynı kökende başka bir kodun kendi önbellekleri olabilir ve yalnızca kendinize ait olanları silmelisiniz.
async function eskiOnbellekleriSil(guncel) {
const adlar = await caches.keys();
for (const ad of adlar) {
const bizim = ad.startsWith("myapp-");
if (ad === guncel || !bizim) continue;
await caches.delete(ad);
}
}Sürümlemenin diğer yarısı yeni önbelleği doldurmaktır. MDN örneklerinde bu iş service worker'ın install olayında yapılır: event.waitUntil() içine verilen promise, caches.open() ve addAll() tamamlanana kadar kurulumu bekletir. addAll() listedeki tek bir adres bile başarısız olursa reddedildiği için kurulum da başarısız sayılır ve yarım dolmuş bir önbellekle çalışan bir service worker ortaya çıkmaz. Bu davranış istenen bir güvencedir, ama listeye yazım hatalı bir adres eklerseniz yeni sürüm hiç kurulmaz; listeyi kısa tutmak ve dağıtımdan önce her adresin 200 döndürdüğünü kontrol etmek bu yüzden önemlidir.
self.addEventListener("install", (event) => {
event.waitUntil(
caches.open("kabuk-v2").then((cache) =>
cache.addAll(["/", "/index.html", "/style.css", "/app.js"]),
),
);
});Sayfada Cache API kullanımı
Cache API yalnızca service worker'a özgü değildir. Bir sayfa, örneğin bir API yanıtını önce önbellekte arayıp yoksa ağdan alabilir. MDN'in Window.caches örneği tek satırdır: caches.open("v1").then((cache) => cache.match("/list")). Daha eksiksiz bir akış şöyle kurulabilir:
async function veriyiGetir(url) {
const ad = "myapp-1";
const cache = await caches.open(ad);
const kayitli = await cache.match(url);
if (kayitli && kayitli.ok) {
return kayitli.json();
}
await cache.add(url); // 200 dışı yanıtta TypeError ile reddedilir
const yeni = await cache.match(url);
await eskiOnbellekleriSil(ad);
return yeni.json();
}Burada ağ isteği fetch() ile yapılır ve CORS kurallarına tabidir; farklı kökenden gelen yanıtın okunabilmesi için sunucunun izin vermesi gerekir (bkz. CORS rehberi). fetch() kullanımının temelleri için Fetch API ile GET ve POST yazısına bakabilirsiniz.
Mini örnek: kendi ürettiğiniz yanıtı saklamak
put() ağdan gelmemiş bir yanıtı da kabul eder, çünkü Response nesnesini kendiniz oluşturabilirsiniz. Bu, ağ yokken gösterilecek basit bir yedek sayfa veya varsayılan bir JSON için kullanışlıdır. Aşağıdaki örnekte uygulama ilk açıldığında küçük bir çevrimdışı sayfası ve boş bir ayar yanıtı önbelleğe yazılıyor; daha sonra ağ isteği başarısız olursa bu kayıtlar kullanılıyor. Anahtar olarak yalnızca http veya https şemalı URL'ler kabul edilir; MDN'e göre başka bir şema TypeError verir. Göreli adresler sayfanın adresine göre çözüldüğü için /cevrimdisi.html gibi bir yol yeterlidir.
async function yedekleriHazirla() {
const cache = await caches.open("yedek-v1");
await cache.put(
"/cevrimdisi.html",
new Response("<h1>Şu anda çevrimdışısınız</h1>", {
headers: { "Content-Type": "text/html; charset=utf-8" },
}),
);
await cache.put(
"/api/ayarlar",
new Response(JSON.stringify({ tema: "acik" }), {
headers: { "Content-Type": "application/json" },
}),
);
}
async function ayarlariGetir() {
try {
const yanit = await fetch("/api/ayarlar");
if (yanit.ok) return yanit.json();
} catch {
// ağ hatası: önbelleğe düş
}
const kayitli = await caches.match("/api/ayarlar", { cacheName: "yedek-v1" });
return kayitli ? kayitli.json() : null;
}Dikkat edilecek nokta, fetch() çağrısının yalnızca ağ hatasında reddedilmesidir. MDN'in Cache örneği de vurgular: 404 gibi bir HTTP hata yanıtı istisna oluşturmaz, normal bir yanıt nesnesi olarak gelir. Bu yüzden yukarıdaki kod hem catch bloğunu hem de yanit.ok kontrolünü kullanır; ikisinden biri eksik olursa sunucu hata verdiğinde yedek kayıt hiç devreye girmez.
Kota, temizlik ve çerezler
Cache API verisi, IndexedDB ile aynı köken kotasını paylaşır. MDN, her tarayıcının bir kökenin kullanabileceği önbellek alanı için kesin bir üst sınırı olduğunu, kullanım tahmininin StorageManager.estimate() ile alınabileceğini ve tarayıcının gerekirse bir kökenin tüm önbellek verisini silebileceğini belirtir; genellikle ya hepsini ya hiçbirini siler. Önbellekleri düzenli temizlemek sizin sorumluluğunuzdadır. Kotayı ölçmeyi depolama kotası yazısında anlatıyoruz.
Bir başka ince nokta çerezlerle ilgilidir. Fetch API, fetch() tarafından döndürülen yanıttan Set-Cookie başlıklarını çıkarır. Bu yüzden önbellekte saklanan bir yanıt Set-Cookie içermez ve önbellekten sunulduğunda çerez kurmaz. Çerezlerin teknik ayrıntısı document.cookie ve Set-Cookie yazısındadır.
Sık yapılan hatalar

- match() sonucunu kontrol etmemek: Eşleşme yoksa sonuç
undefined'dır; doğrudan.json()çağırmak hata verir. - clone() olmadan put(): Gövde bir kez okunabildiği için aynı yanıtı hem saklayıp hem kullanamazsınız.
- Cache API'nin süre dolmasını beklemek: HTTP başlıkları dikkate alınmaz; eski kayıtları sürümleme ve silme ile siz yönetirsiniz.
- add() ile opak yanıt saklamaya çalışmak:
no-corsyanıtının durumu 0 göründüğü içinadd()reddedilir; gerekiyorsaput()kullanılır ama içeriği doğrulayamazsınız. - POST isteklerini eşleştirmeyi beklemek: Varsayılan eşleştirme GET ve HEAD ile sınırlıdır.
- HTTP sayfada denemek:
CacheStoragegüvenilmeyen kökenlerdeSecurityErrorile reddedilir.
Sık Sorulan Sorular
Cache API'yi service worker olmadan kullanabilir miyim?
Evet. MDN, Cache arayüzünün pencere bağlamında da sunulduğunu ve service worker ile birlikte kullanılmak zorunda olmadığını belirtir. Sayfada window.caches üzerinden erişirsiniz; ancak ağ isteklerini araya girerek yanıtlamak için service worker gerekir.
Önbelleğe aldığım dosyalar ne zaman güncellenir?
Siz put(), add() veya delete() çağırmadıkça hiçbir zaman. Yeni dosyaları yeni adlı bir önbelleğe yazıp eskisini silmek en güvenli yoldur.
Cache API ile IndexedDB arasında nasıl seçim yaparım?
Ağdan gelen yanıtları (HTML, CSS, görseller, API yanıtları) olduğu gibi saklamak için Cache API, uygulama verisini sorgulamak ve düzenlemek için IndexedDB uygundur. İkisi aynı kotayı paylaşır.
Önbelleğe alınan yanıt neden bazen bulunmuyor?
En yaygın nedenler sorgu dizesinin farklı olması (çözüm: ignoreSearch), istek metodunun GET olmaması, Vary başlığının eşleşmeyi engellemesi ve tarayıcının disk baskısı nedeniyle kökenin verisini silmiş olmasıdır.
Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.
İlgili içerikler: Web API öğrenme rehberi · Çevrimdışı önbellek stratejileri · Service worker yaşam döngüsü · Request, Response ve Headers · Cache-Control ve ETag · Önbellek nedir