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 off veya proxy_no_cache kullanın
  • Stale cache hayat kurtarır: proxy_cache_use_stale ile backend sorunlarında bile servis vermeye devam edin
  • Thundering herd’i önle: proxy_cache_lock ve proxy_cache_background_update kombinasyonunu 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.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir