Nginx Performans Optimizasyonu: İleri Seviye Rehber

Nginx'te reverse proxy, load balancing, önbellekleme ve rate limiting kurulumunu gerçek konfigürasyon örnekleriyle adım adım, ayrıntılı biçimde anlatıyoruz.

Linux
Nginx Performans Optimizasyonu: İleri Seviye Rehber

Trafiği artan bir sitede en sık duyulan öneri şudur: Apache yetersiz kalıyor, Nginx'e geçin. Bu öneri çoğu zaman haklı ama gerekçesi sanıldığından farklı. Nginx'i hızlı kılan şey gizemli bir teknoloji değil, event-driven mimarisi ve doğru ayarlanmış birkaç direktiftir. Gerçek nginx performans optimizasyonu da bu direktiflerde saklı.

Nginx mimarisi neden bu kadar hızlı

Apache'nin klasik prefork modeli her bağlantı için ayrı bir process ya da thread açar. Bağlantı sayısı binlere çıktığında bu model belleği hızla tüketir, çünkü her process kendi başına bellek ayırır. Nginx ise event-driven, asenkron bir mimariyle çalışır: sabit sayıda worker process, binlerce bağlantıyı tek thread üzerinde event loop ile yönetir.

Bu farkın pratik sonucu nettir: aynı donanımda Nginx çok daha az bellekle çok daha fazla eşzamanlı bağlantı karşılar. Bu modelin ayar noktaları worker_processes ve worker_connections direktifleridir:

worker_processes auto;
events {
    worker_connections 4096;
    use epoll;
}

worker_processes auto değeri, sunucudaki CPU çekirdek sayısı kadar worker başlatır; çoğu kurulum için en iyi varsayılan budur.

worker_connections, her worker'ın açabileceği maksimum bağlantı sayısını belirler. Toplam kapasite worker_processes × worker_connections formülüyle hesaplanır.

Bu fark yük testinde açıkça ortaya çıkar. On bin eşzamanlı bağlantı altında Apache'nin prefork modeli gigabaytlarca bellek harcayabilirken, Nginx aynı yükü çok daha az bellekle taşır. CDN'lerin, reverse proxy katmanlarının ve yüksek trafikli sitelerin önünde bu yüzden neredeyse hep Nginx ya da benzer bir event-driven sunucu durur.

Nginx mi Apache mi: ne zaman hangisi

Bu soru genelde yanlış kurulur. İkisi birbirini dışlamaz; büyük altyapıların çoğunda ikisi bir arada çalışır. Nginx statik dosya sunumu, reverse proxy ve yüksek eşzamanlı bağlantı isteyen senaryolarda öne çıkar. Apache ise .htaccess ile dizin bazlı esnek yapılandırma gerektiren, mod_php gibi modül desteğine ihtiyaç duyan paylaşımlı hosting ortamlarında hâlâ tercih ediliyor.

KriterNginxApache
Eşzamanlı bağlantı performansıYüksek, düşük bellek kullanımıDüşük, prefork'ta bellek maliyeti yüksek
Statik dosya sunumuDoğal olarak hızlıEk modül ayarı gerektirebilir
Dizin bazlı .htaccess desteğiYokVar
Reverse proxy / load balancer olarak kullanımÇok yaygınMümkün ama daha az tercih edilir
Öğrenme eğrisiYapılandırma sözdizimi daha az esnek ama sadeModül ekosistemi geniş, karmaşıklık artabilir

Pratikte en yaygın kurulum katmanlıdır: önde Nginx reverse proxy ve statik dosya sunucusu olarak durur, arkada gerektiğinde Apache ya da doğrudan bir uygulama sunucusu (PHP-FPM, Node.js, Gunicorn) çalışır. Bu düzen, iki sunucunun güçlü yönlerini aynı anda kullanmayı mümkün kılar.

Server ve location blokları ile yapılandırma mantığı

Nginx yapılandırması server bloklarından ve bunların içindeki location bloklarından kurulur. Her server bloğu bir sanal host'u — bir domain ya da subdomain'i — temsil eder; location blokları ise belirli URL yollarına özgü kuralları tanımlar.

Nginx server ve location blok yapılandırmasını gösteren terminal ekranı
server {
    listen 80;
    server_name example.com www.example.com;
    root /var/www/example/public;
    index index.html index.php;

    location / {
        try_files $uri $uri/ =404;
    }

    location ~ \.php$ {
        fastcgi_pass unix:/run/php/php8.2-fpm.sock;
        fastcgi_index index.php;
        include fastcgi_params;
        fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
    }

    location /static/ {
        expires 30d;
        add_header Cache-Control "public, immutable";
    }
}

Yapılandırmayı değiştirdikten sonra sunucuyu doğrudan yeniden başlatmayın. Önce sözdizimini test edin; sözdizimi sorunsuzsa reload komutuyla değişikliği kesintisiz uygulayın:

nginx -t
systemctl reload nginx

nginx -t hatası veren bir dosyayı fark etmeden reload etmek siteyi tamamen çökertebilir. Bu iki komutu ayrı adımlarda çalıştırmak, ihmal edilmemesi gereken bir alışkanlıktır.

Reverse proxy ve load balancing kurulumu

Nginx'in en yaygın kullanım alanlarından biri, arka planda çalışan uygulama sunucularının (Node.js, PHP-FPM, Gunicorn) önünde reverse proxy görevi görmesidir. Gelen isteği arka uca proxy_pass direktifi yönlendirir:

location /api/ {
    proxy_pass http://127.0.0.1:3000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
}

Arka uçta birden fazla sunucu çalışıyorsa yük dengeleme upstream bloğuyla kurulur. Nginx bunun için dört temel algoritma sunuyor: round-robin (varsayılan), least_conn, ip_hash ve ağırlıklı dağıtım.

upstream backend_pool {
    least_conn;
    server 10.0.0.11:3000 weight=3;
    server 10.0.0.12:3000 weight=2;
    server 10.0.0.13:3000 backup;
}

server {
    location / {
        proxy_pass http://backend_pool;
    }
}

least_conn algoritması isteği en az aktif bağlantıya sahip sunucuya gönderir; oturum süresi değişken uygulamalarda round-robin'e göre daha dengeli sonuç verir. backup etiketi taşıyan sunucu, diğerleri çöktüğünde devreye girer. Açık kaynak Nginx'te sağlık kontrolü max_fails ve fail_timeout parametreleriyle sınırlıdır; gerçek anlamda aktif health check yalnızca Nginx Plus'ta bulunuyor.

AlgoritmaDağıtım mantığıEn uygun senaryo
round-robinSırayla eşit dağıtırEşit kapasiteli sunucular
least_connEn az bağlantılı sunucuya yönlendirirDeğişken oturum süresi
ip_hashAynı istemciyi hep aynı sunucuya gönderirOturum yapışkanlığı (sticky session)
ağırlıklı (weight)Kapasiteye göre orantılı dağıtırFarklı donanımlı sunucular

Önbellekleme ile yanıt sürelerini kısaltma

Reverse proxy önbellekleme, arka uca giden istek sayısını azaltarak hem yanıt süresini hem de sunucu yükünü düşürür. Önbellek alanını proxy_cache_path tanımlar; bu önbelleğin hangi location'da kullanılacağını ise proxy_cache belirtir:

İsteğin önbellekten mi kaynaktan mı karşılandığını gösteren akış şeması
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=api_cache:10m max_size=1g inactive=60m;

server {
    location /api/ {
        proxy_pass http://backend_pool;
        proxy_cache api_cache;
        proxy_cache_valid 200 10m;
        proxy_cache_valid 404 1m;
        add_header X-Cache-Status $upstream_cache_status;
    }
}

X-Cache-Status başlığı bir yanıtın önbellekten mi (HIT) yoksa arka uçtan mı (MISS) geldiğini gösterir; önbellek performansını doğrulamanın en hızlı yolu budur. Statik dosyalarda tarayıcı önbelleğini expires direktifiyle ayarlamak, sunucu yükünü azaltan ayrı bir katmandır.

Açık kaynak Nginx'te önbellek geçersizleştirmek için doğrudan bir purge komutu yok. Bunun yerine iki yol izlenir: kısa proxy_cache_valid süreleri tanımlamak ya da içerik güncellendiğinde önbellek anahtarına bir versiyon parametresi eklemek.

Sıkıştırma ve statik dosya sunumunu hızlandırma

Gzip sıkıştırma, HTML, CSS, JS ve JSON gibi metin tabanlı içeriklerin boyutunu belirgin biçimde küçültür. Ayarı basittir; en sık yapılan hata, doğru MIME türlerini listeye eklememektir:

Nginx güvenlik yapılandırmasında rate limiting katmanlarını özetleyen şema
gzip on;
gzip_types text/plain text/css application/json application/javascript text/xml application/xml;
gzip_min_length 1024;
gzip_comp_level 5;

Statik dosyalarda sendfile, tcp_nopush ve tcp_nodelay direktifleri, dosyayı kernel alanından doğrudan ağ soketine kopyalatarak CPU yükünü düşürür:

sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
keepalive_requests 1000;

keepalive_timeout, bir istemcinin aynı TCP bağlantısını ne kadar süre yeniden kullanabileceğini belirler. Değeri çok yüksek tutmak worker kapasitesini boşa harcar; çok düşük tutmak ise her istekte yeni bağlantı açma maliyetini geri getirir. 60-75 saniye aralığı çoğu site için makul bir denge kurar.

Brotli ile ek sıkıştırma katmanı

Gzip'in yanına, çoğu modern dağıtımda opsiyonel ngx_brotli modülü de eklenebilir. Brotli, özellikle metin tabanlı içeriklerde gzip'ten daha iyi bir sıkıştırma oranı verir. İkisini birlikte tanımlamak, Brotli destekleyen tarayıcılarda otomatik olarak Brotli'nin tercih edilmesini sağlar.

brotli on;
brotli_types text/plain text/css application/json application/javascript text/xml application/xml;
brotli_comp_level 5;

Modül varsayılan Nginx paketinde bulunmaz; kaynağından derlenmesi ya da dağıtımın paket deposundan (libnginx-mod-http-brotli gibi) kurulması gerekir. brotli bloğu tanımlı olduğu hâlde modül kurulu değilse nginx -t hemen hata verir; bu yüzden kurulumdan sonra test etmek atlanmamalı.

Statik varlıklarda Cache-Control başlığını doğru ayarlamak, sıkıştırma kadar belirleyicidir. Sürüm numarası veya hash içeren dosya adlarında (app.a3f9c1.js gibi) uzun süreli önbellekleme, sürümsüz dosyalarda ise kısa süreli önbellekleme tercih edilir:

location ~* \.(js|css)$ {
    add_header Cache-Control "public, max-age=31536000, immutable";
}

location ~* \.(html)$ {
    add_header Cache-Control "no-cache";
}

immutable direktifi, dosyanın süresi dolana kadar asla değişmeyeceğini tarayıcıya bildirir; tarayıcı bu sayede revalidation isteği bile göndermez. HTML dosyalarında no-cache kullanmak ise her istekte sunucuya sorulmasını, dolayısıyla güncel içeriğin garanti edilmesini sağlar.

Güvenlik yapılandırması ve rate limiting

SSL/TLS terminasyonu çoğunlukla Nginx katmanında yapılır. Let's Encrypt sertifikasıyla temel bir HTTPS bloğu şöyle kurulur:

server {
    listen 443 ssl http2;
    server_name example.com;
    ssl_certificate /etc/letsencrypt/live/example.com/fullchain.pem;
    ssl_certificate_key /etc/letsencrypt/live/example.com/privkey.pem;
    ssl_protocols TLSv1.2 TLSv1.3;
    ssl_prefer_server_ciphers off;
}

http2 parametresi, tek bir TCP bağlantısı üzerinden birden çok isteğin paralel işlenmesini sağlayan HTTP/2 multiplexing'i devreye sokar. Bu, çok sayıda küçük dosya (CSS, JS, ikon) yükleyen sitelerde belirgin bir hız kazancı sağlar.

OCSP stapling ve HSTS ile TLS handshake'i hızlandırma

TLS handshake sırasında tarayıcı, sertifikanın iptal edilip edilmediğini OCSP sunucusuna sorar; bu sorgu ekstra bir gidiş-dönüş gecikmesi ekler. OCSP stapling bu doğrulamayı Nginx'in kendisine yaptırıp yanıta ekler; tarayıcının ayrıca sorgu atmasına gerek kalmaz.

ssl_stapling on;
ssl_stapling_verify on;
ssl_trusted_certificate /etc/letsencrypt/live/example.com/chain.pem;
resolver 8.8.8.8 8.8.4.4 valid=300s;

HSTS (HTTP Strict Transport Security) başlığı, tarayıcıya belirli bir süre boyunca siteye yalnızca HTTPS üzerinden bağlanmasını bildirir; ilk isteğin bile HTTP'ye düşmesinin önüne geçer.

add_header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload" always;

preload seçeneğini dikkatli kullanmak gerekir. Tarayıcıların önceden yüklenmiş HSTS listesine girmek, domaini uzun süreliğine tamamen HTTPS'e kilitler. Bu adımı geri almak zor olduğundan, önce preload olmadan test etmek, sorun çıkmazsa ardından eklemek daha güvenli bir sıradır.

Kaba kuvvet denemelerine ve aşırı istek saldırılarına karşı limit_req modülü devreye girer:

limit_req_zone $binary_remote_addr zone=login_limit:10m rate=5r/m;

location /login {
    limit_req zone=login_limit burst=3 nodelay;
}

Bu kural, aynı IP adresinden /login yoluna dakikada 5'ten fazla istek gelmesini engeller. burst=3 ise kısa süreli ani yükleri belirli bir ölçüde tolere eder.

Rate limiting kurarken gözden kaçırılmaması gereken birkaç nokta var:

  • zone boyutu (10m) yaklaşık 160 bin benzersiz IP adresini bellekte tutar.
  • nodelay kullanılmazsa istekler kuyruğa alınır ve gecikme artar.
  • Proxy arkasında çalışan sitelerde gerçek istemci IP'si için $binary_remote_addr yerine X-Forwarded-For başlığı kullanılmalı.
  • API endpoint'lerine ayrı zone tanımlamak, login trafiğiyle genel trafiği birbirinden bağımsız sınırlara ayırır.

Güvenlik başlıklarını da aynı bloklara eklemek gerekir:

add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;

Log analizi ile sorun tespiti

Nginx'in access ve error logları, performans sorunlarını teşhis etmede ilk bakılacak yerdir. Debian/Ubuntu tabanlı dağıtımlarda varsayılan konumları /var/log/nginx/access.log ve /var/log/nginx/error.log'dur.

# En cok istek gonderen IP adreslerini listeleme
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20

# Son 50 hata satirini canli izleme
tail -f /var/log/nginx/error.log

Yanıt süresini loglara eklemek, hangi endpoint'in yavaşladığını görmeyi kolaylaştırır. Bunun için log_format direktifine $request_time ve $upstream_response_time değişkenlerini eklemek yeterlidir:

log_format timed '$remote_addr - $request_time $upstream_response_time '
                  '"$request" $status "$http_user_agent"';
access_log /var/log/nginx/access.log timed;

Log dosyaları büyüdükçe elle taramak pratik olmaktan çıkar; logrotate ile günlük rotasyon kurmak büyük siteler için işe yarar bir çözümdür. Hata loglarında sık tekrarlayan upstream timed out mesajları genelde arka uç uygulamanın yavaşladığına, connect() failed mesajları ise arka ucun tamamen çöktüğüne işaret eder.

goaccess ile anlık trafik panosu

goaccess, Nginx loglarını terminalde ya da tarayıcıda görsel bir panoya dönüştüren açık kaynak bir araçtır. Çoğu dağıtımda kurulumu tek komutla biter ve mevcut log formatını çoğunlukla otomatik tanır.

sudo apt install goaccess
goaccess /var/log/nginx/access.log --log-format=COMBINED -o /var/www/report.html --real-time-html

--real-time-html bayrağı, oluşan HTML raporunu WebSocket üzerinden canlı günceller; log dosyası büyüdükçe sayfa kendiliğinden yenilenir. Bu rapor en çok istek alan sayfaları, 404 üreten yolları, tarayıcı ve işletim sistemi dağılımını tek ekranda toplar.

Özel bir log_format kullanıyorsanız (yukarıdaki timed formatı gibi), goaccess bunu tanımayabilir; bu durumda --log-format parametresine kendi formatınızın karşılığını elle tanımlamak gerekir. goaccess çıktısını düzenli aralıklarla üretmek için bir cron görevi kurmak, raporu elle çalıştırmayı akıldan çıkarma riskini ortadan kaldırır.

Resmî Nginx dokümantasyonu tüm direktiflerin tam listesini ve varsayılan değerlerini içerir; yapılandırma yaparken buraya bakmak, forumlarda dolaşan eski ve zaman zaman hatalı örneklere güvenmekten daha sağlamdır (nginx.org/en/docs). Yapılandırmayı her değiştirdiğinizde nginx -t ile test etmek, üretim ortamında beklenmedik kesintilerin önüne geçen en basit alışkanlıktır.

Celil Uyanikoglu

Yazan Celil Uyanikoglu

Bilgisayar mühendisiyim; 25 yılı aşkın süredir bilgi işlem sektörünün içindeyim. Bu blogu 2020'de, işimde her gün karşılaştığım sorunların çözümlerini bir yere yazmak için açtım: Linux, güvenlik, tarayıcılar, yapay zeka araçları. Yazdığım her rehberi önce kendi bilgisayarımda ya da sunucumda deniyorum; çalıştığını görmediğim adımı yayınlamam. Hatalı ya da eskimiş bir şey görürsen iletişim sayfasından yaz — düzeltirim.

Yorum

Henüz yorum yok.

Sohbete katıl. Yorumlar yayınlanmadan önce moderasyondan geçer.

Yorum yap

E-posta adresin yayınlanmaz. Yorumlar moderasyondan sonra yayınlanır.

Sırada

İlgili notlar