Merhaba arkadaşlar, bugün başımı çok ağrıtan bir sorunu ve nasıl çözdüğümü anlatacağım. Bir Next.js projesinde, `public` klasöründeki font dosyaları, ikonlar ve sık değişmeyen asset'ler için performans optimizasyonu yapmam gerekiyordu. Bu dosyalar her kullanıcı isteğinde tekrar tekrar indirilince, hem sunucu yükü artıyordu hem de kullanıcı deneyimi yavaşlıyordu. İşte benim bulduğum en temiz çözüm: agresif ama akıllıca bir caching stratejisi.
Sorunun Can Alıcı Noktası
Next.js, `public` klasöründeki dosyaları olduğu gibi sunar. Varsayılan olarak, bu dosyalar için özel bir `Cache-Control` header'ı belirlemezsiniz. Bu da tarayıcıların bu dosyaları ne kadar süreyle önbellekte tutacağının (cache) belirsiz olmasına neden olur. Genellikle tarayıcı kendi heuristics (sezgisel) kurallarıyla çalışır, bu da tutarsız sonuçlar doğurur. Özellikle font (.woff2, .ttf) ve logo (.svg, .png) dosyaları her sayfa yüklenişinde tekrar isteniyordu. Bu hatayı ilk gördüğümde kafayı yemiştim, çünkü Lighthouse skorlarım düşüktü.
Çözüm: next.config.js ile Cache-Control Header'larını Yönetmek
Çözüm, `next.config.js` dosyasında `headers` fonksiyonunu kullanmaktan geçiyor. Bu fonksiyon sayesinde, belirli path'ler (yollar) için özel HTTP header'ları ekleyebiliyoruz. Benim stratejim, dosya türlerine göre farklı cache süreleri belirlemek oldu.
Yukarıdaki kodu açıklayayım. `source` kısmında dosya uzantılarına göre pattern'ler (desenler) tanımladım. `headers` dizisi içinde ise her biri için farklı bir `Cache-Control` değeri belirledim.
Cache-Control Değerlerimin Anlamı
`public`: İçeriğin herkes (tarayıcı, CDN, ara proxy'ler) tarafından cache'lenebileceğini söyler.
`immutable`: Bu dosyanın içeriğinin asla değişmeyeceğini belirtir. Tarayıcı, cache süresi dolana kadar bu dosyayı tekrar sunucuya sormaz. Bu, sadece dosya adına hash (örn: font.abc123.woff2) eklediğiniz dosyalar için güvenlidir. Eğer hash kullanmıyorsanız, bu değeri kaldırın.
`max-age=31536000`: Cache'in maksimum yaşını saniye cinsinden belirtir (31536000 saniye = 365 gün).
`stale-while-revalidate=86400`: Cache süresi (`max-age`) dolduktan sonraki 86400 saniye (1 gün) boyunca, tarayıcı eski (stale) cache'lenmiş versiyonu kullanmaya devam eder, arkada planda[/COLOR] sunucudan yeni versiyonu alıp cache'i günceller. Bu, kullanıcıya anında içerik sunarken arka planda güncelleme yapmayı sağlar. Müthiş bir özellik!
`must-revalidate`: Cache süresi dolduğunda, tarayıcının mutlaka sunucuya danışması gerektiğini söyler. Daha dinamik içerikler için kullandım.
Sonuçlar ve Dikkat Edilmesi Gerekenler
Bu konfigürasyonu canlıya aldıktan sonraki sonuçlar göz kamaştırıcıydı. Lighthouse "Best Practices" ve "Performance" skorlarında ciddi artışlar oldu. Sunucuya gelen gereksiz istek sayısı büyük ölçüde azaldı.
Ancak DİKKAT! Bu kadar agresif caching yaparken, dosyalarınızı version'lama (file hashing) stratejiniz olmazsa elinize yüzünüze bulaştırırsınız. Kullanıcılar eski font/ikon dosyalarını görmeye devam eder. Eğer dosya adını değiştirmeden (`logo.png` -> `logo.png`) `public` klasöründeki bir dosyayı güncellerseniz, kullanıcılar cache süresi dolana kadar (1 yıl!) eski dosyayı görmeye devam edecektir. Çözüm, build sürecinize dosya adına hash eklemek veya dosya adını manuel değiştirmektir (örn: `logo-v2.png`).
Siz Next.js projelerinizde static asset'leri nasıl cache'liyorsunuz? Benim yöntemim çok mu agresif geldi, yoksa sizin de benzer taktikleriniz var mı? CDN (Cloudflare, Vercel) kullanıyorsanız, onların cache kuralları ile nasıl bir uyum sağladınız? Yorumlarda deneyimlerinizi paylaşın!
Next.js, `public` klasöründeki dosyaları olduğu gibi sunar. Varsayılan olarak, bu dosyalar için özel bir `Cache-Control` header'ı belirlemezsiniz. Bu da tarayıcıların bu dosyaları ne kadar süreyle önbellekte tutacağının (cache) belirsiz olmasına neden olur. Genellikle tarayıcı kendi heuristics (sezgisel) kurallarıyla çalışır, bu da tutarsız sonuçlar doğurur. Özellikle font (.woff2, .ttf) ve logo (.svg, .png) dosyaları her sayfa yüklenişinde tekrar isteniyordu. Bu hatayı ilk gördüğümde kafayı yemiştim, çünkü Lighthouse skorlarım düşüktü.
Çözüm, `next.config.js` dosyasında `headers` fonksiyonunu kullanmaktan geçiyor. Bu fonksiyon sayesinde, belirli path'ler (yollar) için özel HTTP header'ları ekleyebiliyoruz. Benim stratejim, dosya türlerine göre farklı cache süreleri belirlemek oldu.
JavaScript:
// next.config.js
module.exports = {
async headers() {
return [
{
// Public klasöründeki TÜM dosyalar için geçerli source
source: '/:path',
headers: [
{
key: 'Cache-Control',
// Varsayılan, daha düşük öncelikli header. Dinamik içerikler için.
value: 'public, max-age=0, must-revalidate',
},
],
},
{
// 1. GRUP: SIK DEĞİŞMEYEN ASSET'LER (Fontlar, logolar, temel ikonlar)
// Bu dosyalara versioning (dosya adına hash ekleme) yapıyorsanız süre çok uzun olabilir.
source: '/:path(woff|woff2|ttf|otf|eot|svg|png|jpg|jpeg|ico|webp)',
headers: [
{
key: 'Cache-Control',
// 1 YIL cache. Agresif ama güvenli. Dosya adı değişirse yeni dosya istenir.
value: 'public, immutable, max-age=31536000, stale-while-revalidate=86400',
},
],
},
{
// 2. GRUP: BİRAZ DAHA SIK GÜNCELLENEBİLECEK STATİK DOSYALAR
// Manifest, sitemap gibi dosyalar.
source: '/(manifest.json|sitemap.xml|robots.txt)',
headers: [
{
key: 'Cache-Control',
// 1 saat cache, ardından doğrula.
value: 'public, max-age=3600, stale-while-revalidate=7200',
},
],
},
];
},
};
Yukarıdaki kodu açıklayayım. `source` kısmında dosya uzantılarına göre pattern'ler (desenler) tanımladım. `headers` dizisi içinde ise her biri için farklı bir `Cache-Control` değeri belirledim.
`public`: İçeriğin herkes (tarayıcı, CDN, ara proxy'ler) tarafından cache'lenebileceğini söyler.
`immutable`: Bu dosyanın içeriğinin asla değişmeyeceğini belirtir. Tarayıcı, cache süresi dolana kadar bu dosyayı tekrar sunucuya sormaz. Bu, sadece dosya adına hash (örn: font.abc123.woff2) eklediğiniz dosyalar için güvenlidir. Eğer hash kullanmıyorsanız, bu değeri kaldırın.
`max-age=31536000`: Cache'in maksimum yaşını saniye cinsinden belirtir (31536000 saniye = 365 gün).
`stale-while-revalidate=86400`: Cache süresi (`max-age`) dolduktan sonraki 86400 saniye (1 gün) boyunca, tarayıcı eski (stale) cache'lenmiş versiyonu kullanmaya devam eder, arkada planda[/COLOR] sunucudan yeni versiyonu alıp cache'i günceller. Bu, kullanıcıya anında içerik sunarken arka planda güncelleme yapmayı sağlar. Müthiş bir özellik!
`must-revalidate`: Cache süresi dolduğunda, tarayıcının mutlaka sunucuya danışması gerektiğini söyler. Daha dinamik içerikler için kullandım.
Bu konfigürasyonu canlıya aldıktan sonraki sonuçlar göz kamaştırıcıydı. Lighthouse "Best Practices" ve "Performance" skorlarında ciddi artışlar oldu. Sunucuya gelen gereksiz istek sayısı büyük ölçüde azaldı.
Ancak DİKKAT! Bu kadar agresif caching yaparken, dosyalarınızı version'lama (file hashing) stratejiniz olmazsa elinize yüzünüze bulaştırırsınız. Kullanıcılar eski font/ikon dosyalarını görmeye devam eder. Eğer dosya adını değiştirmeden (`logo.png` -> `logo.png`) `public` klasöründeki bir dosyayı güncellerseniz, kullanıcılar cache süresi dolana kadar (1 yıl!) eski dosyayı görmeye devam edecektir. Çözüm, build sürecinize dosya adına hash eklemek veya dosya adını manuel değiştirmektir (örn: `logo-v2.png`).
Siz Next.js projelerinizde static asset'leri nasıl cache'liyorsunuz? Benim yöntemim çok mu agresif geldi, yoksa sizin de benzer taktikleriniz var mı? CDN (Cloudflare, Vercel) kullanıyorsanız, onların cache kuralları ile nasıl bir uyum sağladınız? Yorumlarda deneyimlerinizi paylaşın!