Nginx Caching ile API Performansını Artırma
API’lerinizin yavaş yanıt vermesi, kullanıcı deneyimini mahveden ve altyapı maliyetlerinizi artıran en yaygın sorunlardan biridir. Özellikle yüksek trafikli sistemlerde, her istek için backend’e gitmek hem veritabanınızı hem de uygulama sunucularınızı gereksiz yere yorar. Nginx’in güçlü caching mekanizmaları sayesinde bu yükü dramatik biçimde azaltabilir, API yanıt sürelerini milisaniyelere indirebilirsiniz. Bu yazıda gerçek dünya senaryolarıyla Nginx caching’i derinlemesine inceleyeceğiz.
Nginx Caching Neden Bu Kadar Önemli?
Bir e-ticaret platformu düşünün. Ürün listesi API’si günde milyonlarca kez çağrılıyor, ancak ürün verileri her 5 dakikada bir değişiyor. Bu durumda her istek için veritabanına gitmek tamamen gereksiz. Nginx proxy cache devreye girdiğinde, ilk istek backend’e gider ve yanıt cache’e yazılır. Sonraki tüm istekler doğrudan cache’den karşılanır.
Nginx caching’in sağladığı temel faydalar:
- Gecikme azaltımı: Disk veya memory’den servis edilen yanıtlar, backend işlemesine kıyasla 10-100 kat daha hızlı olabilir
- Backend yük azaltımı: Database query’leri ve uygulama mantığı tekrar çalıştırılmaz
- Maliyet tasarrufu: Daha az compute kaynağı kullanımı, daha düşük cloud faturası
- Yüksek erişilebilirlik: Backend geçici olarak çevrimdışı olsa bile cache’lenmiş yanıtlar servis edilebilir
Temel Nginx Cache Yapılandırması
Önce basit bir yapılandırmayla başlayalım. Nginx’te caching iki ana direktifle yönetilir: proxy_cache_path ve proxy_cache.
# /etc/nginx/nginx.conf
http {
# Cache zone tanımlaması - bu blok http context'inde olmalı
proxy_cache_path /var/cache/nginx/api
levels=1:2
keys_zone=api_cache:10m
max_size=1g
inactive=60m
use_temp_path=off;
include /etc/nginx/conf.d/*.conf;
}
Bu yapılandırmadaki parametrelerin anlamları:
- levels=1:2: Cache dizin yapısı. İki seviyeli hiyerarşi, dosya sistemi performansını artırır
- keys_zone=api_cache:10m: Cache zone adı ve metadata için ayrılan shared memory boyutu (10MB yaklaşık 80.000 key tutabilir)
- max_size=1g: Toplam cache boyutu sınırı
- inactive=60m: Bu süre boyunca erişilmeyen cache girdileri silinir
- use_temp_path=off: Temp dizin kullanımını devre dışı bırakır, daha iyi I/O performansı sağlar
Şimdi cache dizinini oluşturalım:
# Cache dizini oluştur ve izinleri ayarla
sudo mkdir -p /var/cache/nginx/api
sudo chown nginx:nginx /var/cache/nginx/api
sudo chmod 700 /var/cache/nginx/api
# Nginx konfigürasyonunu test et
sudo nginx -t
# Nginx'i yeniden yükle
sudo systemctl reload nginx
Gerçek Dünya Senaryosu: Ürün API’si için Cache Yapılandırması
Bir e-ticaret uygulaması için eksiksiz bir Nginx yapılandırması yapalım. Backend’imizde Node.js veya Python ile yazılmış bir API var ve bunu Nginx arkasına alıyoruz.
# /etc/nginx/conf.d/product-api.conf
upstream product_backend {
server 127.0.0.1:3000;
server 127.0.0.1:3001;
keepalive 32;
}
server {
listen 80;
server_name api.example.com;
# Loglama - cache hit/miss takibi için
log_format cache_log '$remote_addr - $upstream_cache_status [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'rt=$request_time uct=$upstream_connect_time '
'uht=$upstream_header_time urt=$upstream_response_time';
access_log /var/log/nginx/product-api-access.log cache_log;
# Genel cache ayarları
proxy_cache api_cache;
proxy_cache_valid 200 302 5m;
proxy_cache_valid 404 1m;
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
proxy_cache_background_update on;
proxy_cache_lock on;
# Cache bypass header'ı ekle - debug için
add_header X-Cache-Status $upstream_cache_status;
location /api/v1/products {
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_valid 200 5m;
proxy_pass http://product_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /api/v1/categories {
# Kategoriler daha az değişir, daha uzun cache
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_valid 200 30m;
proxy_pass http://product_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
location /api/v1/cart {
# Sepet kişiseldir, ASLA cache'leme
proxy_cache off;
proxy_pass http://product_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
Burada dikkat etmemiz gereken kritik nokta: kişiselleştirilmiş veriler (sepet, kullanıcı profili, sipariş geçmişi) asla cache’lenmemeli. proxy_cache off direktifiyle bu endpoint’leri cache’in dışında tutuyoruz.
Cache Key Stratejileri
Cache key, bir isteği benzersiz şekilde tanımlayan string’dir. Yanlış cache key kullanımı ciddi sorunlara yol açabilir.
# /etc/nginx/conf.d/cache-keys.conf
server {
listen 80;
server_name api.example.com;
# Senaryo 1: Temel cache key (URL bazlı)
location /api/v1/public/ {
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache api_cache;
proxy_cache_valid 200 10m;
proxy_pass http://backend;
}
# Senaryo 2: Query string dahil cache key
# /api/v1/search?q=laptop&page=2 ve /api/v1/search?q=phone&page=1 farklı cache entry'leri olmalı
location /api/v1/search {
# $args query string parametrelerini içerir
proxy_cache_key "$scheme$request_method$host$uri$is_args$args";
proxy_cache api_cache;
proxy_cache_valid 200 2m;
proxy_pass http://backend;
}
# Senaryo 3: Dil/lokalizasyon bazlı cache
# Accept-Language header'ına göre farklı cache
location /api/v1/content {
proxy_cache_key "$scheme$request_method$host$request_uri$http_accept_language";
proxy_cache api_cache;
proxy_cache_valid 200 15m;
proxy_pass http://backend;
}
# Senaryo 4: API versiyonu bazlı cache
location /api/ {
proxy_cache_key "$scheme$request_method$host$request_uri$http_x_api_version";
proxy_cache api_cache;
proxy_cache_valid 200 5m;
proxy_pass http://backend;
}
}
Cache Bypass ve Conditional Caching
Her durumda cache kullanmak istemeyebilirsiniz. Örneğin, admin kullanıcılar her zaman taze veri görmeli veya belirli header’lar varsa cache atlanmalı.
# /etc/nginx/conf.d/conditional-cache.conf
# Map direktifi ile cache bypass koşulları belirleme
map $http_cache_control $bypass_cache {
"no-cache" 1;
"no-store" 1;
default 0;
}
map $http_authorization $no_cache_for_auth {
# Bearer token ile gelen admin istekleri cache'i bypass etsin
"~*^Bearer admin_" 1;
default 0;
}
server {
listen 80;
server_name api.example.com;
location /api/v1/products {
# Cache bypass koşulları
proxy_cache_bypass $bypass_cache $no_cache_for_auth $arg_nocache;
proxy_no_cache $bypass_cache $no_cache_for_auth;
proxy_cache api_cache;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_valid 200 5m;
proxy_pass http://backend;
# Hangi cache kararının verildiğini client'a bildir
add_header X-Cache-Status $upstream_cache_status;
add_header X-Cache-Key "$scheme$request_method$host$request_uri";
}
}
$upstream_cache_status değişkeni çok kullanışlıdır. Alabileceği değerler:
- HIT: Yanıt cache’den geldi
- MISS: Cache’de yoktu, backend’den alındı
- BYPASS: Cache bypass edildi
- EXPIRED: Cache’de vardı ama süresi dolmuştu
- STALE: Güncel olmayan cache yanıtı kullanıldı
- UPDATING: Cache güncellenirken eski yanıt kullanıldı
- REVALIDATED: Cache yeniden doğrulandı
Stale Cache ve Yüksek Erişilebilirlik
proxy_cache_use_stale direktifi, backend’in çökmesi durumunda bile kullanıcılara eski cache’lenmiş yanıtları servis etmenizi sağlar. Bu özellik production sistemlerde hayat kurtarır.
# /etc/nginx/conf.d/resilient-api.conf
proxy_cache_path /var/cache/nginx/api
levels=1:2
keys_zone=api_cache:10m
max_size=2g
inactive=24h
use_temp_path=off;
server {
listen 443 ssl http2;
server_name api.example.com;
ssl_certificate /etc/letsencrypt/live/api.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/api.example.com/privkey.pem;
location /api/ {
proxy_cache api_cache;
proxy_cache_key "$scheme$request_method$host$request_uri";
# Normal cache süresi
proxy_cache_valid 200 10m;
# Backend sorunluysa stale cache kullan
# updating: cache güncellenirken eski yanıtı servis et (thundering herd önler)
# error: backend bağlantı hatası
# timeout: backend zaman aşımı
# http_500, http_502, http_503, http_504: backend HTTP hataları
proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504;
# Arka planda cache'i güncelle, kullanıcıyı beklettirme
proxy_cache_background_update on;
# Aynı anda sadece bir istek backend'e gitsin (thundering herd protection)
proxy_cache_lock on;
proxy_cache_lock_timeout 5s;
proxy_cache_lock_age 10s;
# Backend timeout ayarları
proxy_connect_timeout 5s;
proxy_read_timeout 30s;
proxy_send_timeout 30s;
proxy_pass http://backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
add_header X-Cache-Status $upstream_cache_status always;
}
}
Cache Temizleme (Cache Purge)
Cache’deki verileri manuel olarak temizlemek için birkaç yöntem var. Nginx Plus’ta yerleşik purge API’si bulunuyor, ancak açık kaynak versiyonda alternatif yöntemler kullanmamız gerekiyor.
# Yöntem 1: Cache dizinini temizle (tüm cache'i sil)
sudo find /var/cache/nginx/api -type f -delete
# Yöntem 2: Belirli bir URL'nin cache dosyasını bul ve sil
# Cache key MD5 hash'ini hesapla
echo -n "GET https://api.example.com/api/v1/products" | md5sum
# Yöntem 3: Proxy cache purge modülü ile (derleme gerektirir)
# nginx-cache-purge scripti ile belirli URL'yi temizle
cat << 'EOF' > /usr/local/bin/nginx-cache-purge.sh
#!/bin/bash
# Kullanım: nginx-cache-purge.sh /var/cache/nginx/api "GET https://api.example.com/api/v1/products"
CACHE_DIR=$1
CACHE_KEY=$2
if [ -z "$CACHE_DIR" ] || [ -z "$CACHE_KEY" ]; then
echo "Kullanim: $0 <cache_dir> <cache_key>"
exit 1
fi
# MD5 hash hesapla
HASH=$(echo -n "$CACHE_KEY" | md5sum | cut -d' ' -f1)
# Nginx cache path yapısı: son 2 karakter / son 2 karakter hariç son 2 karakter / tam hash
DIR1="${HASH: -1:1}"
DIR2="${HASH: -3:2}"
CACHE_FILE="$CACHE_DIR/$DIR1/$DIR2/$HASH"
if [ -f "$CACHE_FILE" ]; then
rm -f "$CACHE_FILE"
echo "Cache silindi: $CACHE_FILE"
else
echo "Cache dosyasi bulunamadi: $CACHE_FILE"
fi
EOF
chmod +x /usr/local/bin/nginx-cache-purge.sh
Nginx Plus kullanıyorsanız, purge API’si çok daha kolay:
# /etc/nginx/conf.d/cache-purge.conf (Nginx Plus)
# Purge endpoint sadece dahili ağdan erişilebilir olmalı
server {
listen 8080;
server_name localhost;
location /purge/ {
allow 127.0.0.1;
allow 10.0.0.0/8;
deny all;
proxy_cache_purge api_cache "$scheme$request_method$host$request_uri";
}
}
# Kullanım:
# curl -X PURGE http://localhost:8080/purge/api/v1/products
Performans İzleme ve Cache Etkinliği Ölçümü
Cache’in ne kadar etkili çalıştığını izlemek için log analizi yapmalısınız.
# Cache hit oranını hesapla
# /var/log/nginx/access.log dosyasında X-Cache-Status header'ı varsa:
# Son 1000 istek için cache istatistikleri
tail -1000 /var/log/nginx/product-api-access.log |
grep -oP '(HIT|MISS|BYPASS|EXPIRED|STALE|UPDATING)' |
sort | uniq -c | sort -rn
# Zaman bazlı cache hit oranı (son 5 dakika)
awk -v date="$(date -d '5 minutes ago' '+%d/%b/%Y:%H:%M')"
'$0 > date {print}' /var/log/nginx/product-api-access.log |
awk '{
total++
if ($0 ~ /HIT/) hit++
}
END {
print "Total:", total
print "Cache Hit:", hit
print "Hit Rate:", (hit/total)*100 "%"
}'
# Yavaş yanıtları bul (1 saniyeden uzun)
awk '$NF > 1' /var/log/nginx/product-api-access.log |
awk '{print $7, $NF}' | sort -k2 -rn | head -20
Prometheus ve Grafana kullanıyorsanız, nginx-prometheus-exporter ile metrikleri toplayabilirsiniz:
# nginx stub_status modülünü etkinleştir
# /etc/nginx/conf.d/status.conf
server {
listen 127.0.0.1:8080;
location /nginx_status {
stub_status on;
allow 127.0.0.1;
deny all;
}
}
# nginx-prometheus-exporter'ı başlat
docker run -d
--name nginx-exporter
-p 9113:9113
nginx/nginx-prometheus-exporter:latest
--nginx.scrape-uri=http://localhost:8080/nginx_status
Mikro Cache Stratejisi
Yüksek trafikli ve sık güncellenen API’ler için mikro cache (1-2 saniyelik çok kısa süreli cache) kullanmak çok etkilidir. Saniyede 1000 istek alan bir endpoint’te 1 saniyelik cache, backend’e sadece 1 istek gitmesini sağlar.
# /etc/nginx/conf.d/microcache.conf
proxy_cache_path /var/cache/nginx/microcache
levels=1:2
keys_zone=microcache:5m
max_size=512m
inactive=10s
use_temp_path=off;
server {
listen 80;
server_name api.example.com;
# Gerçek zamanlı fiyat verisi - çok kısa cache
location /api/v1/prices {
proxy_cache microcache;
proxy_cache_valid 200 1s;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_use_stale updating;
proxy_cache_background_update on;
proxy_cache_lock on;
proxy_pass http://backend;
add_header X-Cache-Status $upstream_cache_status;
add_header Cache-Control "no-store"; # Client'ın cache'lememesini sağla
}
# Anlık sipariş sayacı - 2 saniyelik cache
location /api/v1/stats/live {
proxy_cache microcache;
proxy_cache_valid 200 2s;
proxy_cache_key "$scheme$host$request_uri";
proxy_cache_use_stale updating;
proxy_cache_lock on;
proxy_pass http://backend;
add_header X-Cache-Status $upstream_cache_status;
}
}
Cache ile Güvenlik Konuları
Cache yanlış yapılandırıldığında ciddi güvenlik açıkları oluşabilir. Authentication gerektiren endpoint’lerde veya kişisel veri içeren yanıtlarda cache kullanımına dikkat etmek gerekir.
# /etc/nginx/conf.d/secure-cache.conf
map $http_authorization $is_authenticated {
"" 0;
default 1;
}
map $request_method $is_safe_method {
GET 1;
HEAD 1;
default 0;
}
server {
listen 80;
server_name api.example.com;
location /api/v1/ {
# Sadece GET ve HEAD isteklerini cache'le
proxy_cache_methods GET HEAD;
# Authorization header'ı olan istekleri cache'leme
proxy_cache_bypass $is_authenticated;
proxy_no_cache $is_authenticated;
# Vary header'ına göre farklı cache girdileri oluştur
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache api_cache;
proxy_cache_valid 200 5m;
proxy_pass http://backend;
# Set-Cookie varsa cache'leme (kişisel yanıt işareti)
proxy_ignore_headers Set-Cookie;
# Cache-Control: private içeren yanıtları cache'leme
proxy_hide_header Cache-Control;
}
# Public endpoint'ler - her şeyi cache'le
location /api/v1/public/ {
proxy_cache api_cache;
proxy_cache_valid 200 30m;
proxy_cache_key "$scheme$request_method$host$request_uri$args";
proxy_pass http://backend;
add_header X-Cache-Status $upstream_cache_status;
# CORS headers ekle
add_header Access-Control-Allow-Origin "*";
add_header Access-Control-Allow-Methods "GET, OPTIONS";
}
}
Sonuç
Nginx caching, API performansını artırmak için en maliyet-etkin ve hızlı uygulanabilir çözümlerden biridir. Doğru yapılandırıldığında backend yükünü yüzde 80-90 oranında azaltabilir ve yanıt sürelerini dramatik biçimde iyileştirebilir.
Önemli noktaları özetleyelim:
- Cache key tasarımı kritik: Yanlış cache key, ya veri karışmasına ya da cache’in hiç işe yaramamasına yol açar
- Kişisel verileri asla cache’leme: Authorization gerektiren endpoint’ler için
proxy_cache offveyaproxy_no_cachekullanın - Stale cache hayat kurtarır:
proxy_cache_use_staleile backend sorunlarında bile servis vermeye devam edin - Thundering herd’i önle:
proxy_cache_lockveproxy_cache_background_updatekombinasyonunu kullanın - İzleme şart: Cache hit oranını sürekli takip edin, düşük hit oranı yanlış yapılandırmaya işaret eder
- Mikro cache güçlüdür: 1-2 saniyelik cache bile yüksek trafikli sistemlerde büyük fark yaratır
- Güvenliği ihmal etme: Cache ile ilgili güvenlik kurallarını en baştan belirle
Production’da bu yapılandırmaları adım adım uygulayın, her değişiklikten sonra metrikleri izleyin ve kendi sisteminizin ihtiyaçlarına göre cache sürelerini ve stratejilerini optimize edin. Her API farklıdır; buradaki değerleri körü körüne kopyalamak yerine kendi trafik profilinizi analiz ederek doğru ayarları bulun.
