Sanallaştırma & Bulut

    Nginx'i Yük Dengeleyici Olarak Yapılandırma

    Nginx upstream bloklarıyla yük dengeleme kurulumu ve üretime hazır ayarlar.

    11 dk okuma Güncellendi: 25 Ağustos 2026

    Elinde zaten Nginx varsa, yük dengeleme için ayrı bir yazılım kurmana çoğu zaman gerek yok. Nginx yük dengeleyici olarak çalıştığında gelen HTTP isteklerini arkasındaki sunucu havuzuna dağıtır, çöken sunucuyu bir süreliğine devre dışı bırakır, TLS'i kendi üzerinde sonlandırır ve tek bir yapılandırma dosyasıyla bunların hepsini yapar. Zaten reverse proxy olarak kullandığın proxy_pass satırının bir adım ötesidir; tek fark, tek bir arka uç yerine bir upstream havuzuna işaret etmesidir.

    Bu rehberde sıfırdan çalışan bir Nginx yük dengeleyici kuracağız. Önce upstream bloğunun anatomisini, sonra dağıtım algoritmalarını ve hangisinin hangi uygulamaya uyduğunu, ardından Nginx'in açık kaynak sürümündeki pasif sağlık kontrolü modelini — ki burası en çok yanlış anlaşılan yer — ele alacağız. Sonrasında bağlantı havuzu, zaman aşımları, TLS sonlandırma, gerçek istemci IP'sinin arka uca taşınması ve stream modülüyle TCP/UDP dengeleme var. Yük dengelemenin genel mantığını hiç incelemediysen önce yük dengeleyici nedir yazısını okumanı öneririm; burada doğrudan Nginx tarafına gireceğiz.

    Upstream Bloğu: Yük Dengelemenin Kalbi#

    Nginx'te bir sunucu havuzu http bağlamı içinde tanımlanan upstream bloğuyla oluşturulur. Bu blok, arka uçtaki uygulama sunucularının adreslerini ve her birinin nasıl davranacağını belirleyen seçenekleri tutar. Havuza bir isim verirsin, sonra server bloğu içindeki proxy_pass direktifiyle o ismi çağırırsın.

    # /etc/nginx/conf.d/yukdengeleyici.conf
    
    upstream uygulama_havuzu {
        # Havuzun durumunu worker süreçleri arasında paylaşılan bellekte tut.
        # Bu olmadan her worker kendi sayaçlarını ayrı tutar.
        zone uygulama_havuzu 64k;
    
        server 10.0.0.11:8080 weight=2 max_fails=3 fail_timeout=15s;
        server 10.0.0.12:8080 weight=1 max_fails=3 fail_timeout=15s;
        server 10.0.0.13:8080 weight=1 max_fails=3 fail_timeout=15s;
    
        # Yalnızca diğerlerinin hepsi düştüğünde devreye girer
        server 10.0.0.19:8080 backup;
    }
    
    server {
        listen 80;
        server_name firmaniz.com;
    
        location / {
            proxy_pass http://uygulama_havuzu;
            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;
        }
    }
    

    Buradaki her parametrenin somut bir karşılığı var. weight=2 yazan sunucu, ağırlığı 1 olanların iki katı istek alır; farklı güçte makineleri aynı havuzda toplarken işine yarar. max_fails=3 ve fail_timeout=15s birlikte çalışır: 15 saniyelik pencere içinde bir sunucuya yapılan 3 istek başarısız olursa, Nginx o sunucuyu 15 saniyeliğine "kullanılamaz" işaretler ve havuzdan çıkarır. Süre dolunca tek bir deneme isteği gönderip sunucunun geri geldiğini kontrol eder. backup işaretli sunucu ise normal koşullarda hiç trafik almaz; yalnızca havuzdaki tüm birincil sunucular düştüğünde devreye girer ve genellikle "bakım sayfası" servis eden hafif bir makine olur.

    zone direktifi çoğu rehberde atlanır ama üretimde önemlidir. Nginx birden fazla worker süreciyle çalışır; paylaşılan bellek bölgesi tanımlamazsan her worker hangi sunucunun düştüğünü kendi başına takip eder — sekiz worker'ın varsa bir arıza sekiz kez ayrı ayrı öğrenilir. zone ile sayaçlar ortaklaşır ve tüm worker'lar aynı anda o sunucudan uzak durur.

    Dağıtım Algoritmaları ve Hangisini Seçmelisin#

    Varsayılan algoritma round-robin'dir: istekler sırayla sunuculara dağıtılır. Basit ve öngörülebilirdir ama her isteğin aynı maliyette olduğunu varsayar. Gerçekte bir istek 5 ms, diğeri 4 saniye sürebilir; round-robin bu farkı görmez. Nginx'in sunduğu diğer yöntemler tam olarak bu varsayımı esnetmek için var.

    upstream uygulama_havuzu {
        # En az aktif bağlantısı olan sunucuya gönder
        least_conn;
    
        server 10.0.0.11:8080;
        server 10.0.0.12:8080;
        server 10.0.0.13:8080;
    }
    
    upstream oturumlu_havuz {
        # Aynı istemci IP'si daima aynı sunucuya düşer
        ip_hash;
    
        server 10.0.0.11:8080;
        server 10.0.0.12:8080;
    }
    
    upstream onbellek_havuzu {
        # URI'ye göre tutarlı hash: aynı içerik hep aynı sunucuda
        hash $request_uri consistent;
    
        server 10.0.0.21:8080;
        server 10.0.0.22:8080;
        server 10.0.0.23:8080;
    }
    

    Seçim yaparken şu tabloyu kullanabilirsin:

    AlgoritmaNasıl çalışırNe zaman doğru seçimDikkat
    round-robin (varsayılan)Sırayla dağıtırİstek süreleri birbirine yakınsaUzun istekler dengeyi bozar
    least_connEn az açık bağlantısı olana gönderirİstek süreleri çok değişkense, WebSocket varsaYavaş sunucuya daha az yük biner, bu istenen davranıştır
    ip_hashİstemci IP'sinin hash'ine göre sabitlerOturum sunucuda tutuluyorsaNAT arkasındaki yüzlerce kullanıcı tek sunucuya yığılır
    hash $request_uri consistentURI'ye göre sabitlerÖnbellek sunucularında isabet oranını artırırconsistent olmadan sunucu eklerken tüm dağılım değişir

    consistent anahtar kelimesi tutarlı hash (ketama) kullanır ve havuza bir sunucu eklediğinde yalnızca anahtarların küçük bir bölümü yer değiştirir. Bunu yazmazsan üç sunuculuk havuza dördüncüyü eklediğinde neredeyse tüm URI'ler farklı sunucuya kayar ve önbelleğin bir anda ısınmamış hale gelir.

    Oturum sürekliliği için ip_hash'e mecbur kalıyorsan asıl sorun uygulamanın durumsuz olmamasıdır. Oturumu Redis gibi ortak bir depoya taşıdığında hem least_conn kullanabilir hem de sunucu ekleyip çıkarırken kimseyi sistemden atmazsın; bu ayrım yatay mı dikey mi ölçekleme yazısında anlatılıyor.

    Sağlık Kontrolü: Nginx'in Pasif Modeli#

    Burası açık kaynak Nginx ile ilgili en çok yanlış bilinen konu. Nginx'in ücretsiz sürümünde aktif sağlık kontrolü yoktur. Yani Nginx arka uçlara düzenli aralıklarla "iyi misin?" diye bir istek göndermez. Bunun yerine pasif bir model kullanır: gerçek kullanıcı isteklerinin sonucuna bakar. Bir istek hata verirse sayaç artar, max_fails eşiğine ulaşınca sunucu fail_timeout süresince dışlanır.

    Bunun pratik anlamı şudur: bir sunucu çöktüğünde bunu ilk öğrenen kişi, o sunucuya düşen gerçek bir kullanıcıdır ve o kullanıcı hata alır. proxy_next_upstream ile bu hatayı kullanıcıya göstermeden başka sunucuya devretmeyi sağlayabilirsin:

    location / {
        proxy_pass http://uygulama_havuzu;
    
        # Hangi durumlarda bir sonraki sunucuya geçilsin
        proxy_next_upstream     error timeout http_502 http_503 http_504;
        # En fazla 2 farklı sunucu denensin
        proxy_next_upstream_tries   2;
        # Devretme denemeleri toplam 10 saniyeyi aşmasın
        proxy_next_upstream_timeout 10s;
    }
    

    Bu ayarla bir sunucu bağlantı reddederse ya da 502 dönerse, Nginx aynı isteği sessizce ikinci sunucuya gönderir ve kullanıcı hiçbir şey fark etmez. Ancak dikkat: proxy_next_upstream listesine non_idempotent eklemediğin sürece Nginx POST gibi istekleri tekrar denemez — ve bu doğru davranıştır, çünkü bir ödeme isteğinin iki kez işlenmesi bir hata sayfasından çok daha kötüdür.

    Arka uçların gerçekten sağlıklı olup olmadığını proaktif olarak bilmek istiyorsan iki seçeneğin var: uygulamana /saglik gibi hafif bir uç nokta ekleyip bunu dışarıdan bir izleme sistemiyle yoklamak ya da aktif sağlık kontrolü olan bir yük dengeleyiciye geçmek. HAProxy kurulumu yazısında anlatılan check parametresi tam olarak bu boşluğu doldurur; ikisini birlikte kullanan kurulumlar da yaygındır.

    Bağlantı Havuzu, Zaman Aşımları ve Tampon Ayarları#

    Varsayılan haliyle Nginx, arka uca her istek için yeni bir TCP bağlantısı açar ve isteği bitirince kapatır. Trafik arttıkça bu, sunucularda gereksiz TIME_WAIT birikmesine ve her istekte fazladan bir el sıkışma gecikmesine yol açar. keepalive ile kalıcı bağlantı havuzu açmak, tek satırlık ama etkisi hemen ölçülen bir iyileştirmedir.

    upstream uygulama_havuzu {
        server 10.0.0.11:8080;
        server 10.0.0.12:8080;
    
        # Her worker'ın açık tutacağı boşta bağlantı sayısı
        keepalive 32;
        # Bir bağlantı üzerinden en fazla kaç istek geçsin
        keepalive_requests 1000;
        # Boşta bağlantı ne kadar açık kalsın
        keepalive_timeout 60s;
    }
    
    location / {
        proxy_pass http://uygulama_havuzu;
    
        # keepalive'in çalışması için ikisi de ZORUNLU
        proxy_http_version 1.1;
        proxy_set_header Connection "";
    
        proxy_connect_timeout 5s;
        proxy_send_timeout    60s;
        proxy_read_timeout    60s;
    }
    

    proxy_http_version 1.1 ve boş Connection başlığı olmadan keepalive hiçbir işe yaramaz — Nginx varsayılan olarak arka uca HTTP/1.0 konuşur ve Connection: close gönderir. Bu iki satırı unutmak, keepalive 32 yazdığı halde hiçbir kazanç görmeyen kurulumların birinci sebebidir.

    Zaman aşımlarında yaygın hata proxy_read_timeout değerini çok yükseğe çekmektir. 300 saniyelik bir okuma zaman aşımı, arka uç takıldığında Nginx'in beş dakika boyunca o bağlantıyı tutması demektir; birkaç yüz böyle istek worker'ları tıkar. Uzun süren işler için doğru çözüm zaman aşımını uzatmak değil, işi bir kuyruğa alıp isteği hemen yanıtlamaktır.

    TLS Sonlandırma ve Gerçek İstemci IP'sini Taşımak#

    Çoğu kurulumda HTTPS trafiği yük dengeleyicide sonlandırılır: sertifika Nginx'te durur, Nginx ile arka uçlar arasındaki iç ağ trafiği düz HTTP olarak akar. Bu, sertifika yönetimini tek bir yerde toplar ve arka uçların şifreleme maliyetinden kurtulmasını sağlar.

    server {
        listen 443 ssl;
        http2 on;
        server_name firmaniz.com;
    
        ssl_certificate     /etc/letsencrypt/live/firmaniz.com/fullchain.pem;
        ssl_certificate_key /etc/letsencrypt/live/firmaniz.com/privkey.pem;
        ssl_protocols       TLSv1.2 TLSv1.3;
        ssl_session_cache   shared:SSL:10m;
        ssl_session_timeout 1d;
    
        location / {
            proxy_pass http://uygulama_havuzu;
            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;
        }
    }
    
    server {
        listen 80;
        server_name firmaniz.com;
        return 301 https://$host$request_uri;
    }
    

    Buradaki X-Forwarded-Proto $scheme satırı görünüşte küçük ama olmadığında uygulaman kendisini HTTP üzerinde sanır. Sonuç: bağlantı HTTPS olmasına rağmen uygulama HTTP'ye yönlendirme üretir ve tarayıcı sonsuz yönlendirme döngüsüne girer. WordPress, Laravel ve Django tarafında en sık görülen "çok fazla yönlendirme" hatasının kaynağı budur; uygulama tarafında da proxy başlıklarına güvenmeyi açman gerekir.

    Arka uç sunucuların erişim kayıtlarında artık her istek yük dengeleyicinin IP'sinden geliyormuş gibi görünür. Gerçek ziyaretçi IP'sini geri kazanmak için arka uç Nginx'lerde realip modülünü kullanırsın:

    # Arka uç sunucudaki nginx.conf içinde
    set_real_ip_from 10.0.0.5;      # yük dengeleyicinin iç IP'si
    real_ip_header   X-Forwarded-For;
    real_ip_recursive on;
    

    ⚠️ set_real_ip_from mutlaka yalnızca kendi yük dengeleyicinin adresini içermelidir. Buraya 0.0.0.0/0 yazarsan, herhangi bir ziyaretçi kendi X-Forwarded-For başlığını uydurup kayıtlarında ve IP tabanlı kısıtlamalarında istediği adresi gösterebilir.

    TCP ve UDP Yük Dengeleme: stream Modülü#

    Nginx yalnızca HTTP dengeleyemez. stream modülü ile veritabanı, mail, DNS ya da özel protokol trafiğini de dağıtabilirsin. stream bloğu http bloğunun dışında, nginx.conf dosyasının en üst seviyesinde yer alır.

    # nginx.conf — http {} bloğunun DIŞINDA
    stream {
        upstream postgres_havuzu {
            least_conn;
            server 10.0.0.31:5432 max_fails=2 fail_timeout=10s;
            server 10.0.0.32:5432 max_fails=2 fail_timeout=10s backup;
        }
    
        server {
            listen 5432;
            proxy_pass          postgres_havuzu;
            proxy_connect_timeout 3s;
            proxy_timeout       1h;   # uzun süren veritabanı oturumları için
        }
    
        # UDP örneği: DNS trafiği
        upstream dns_havuzu {
            server 10.0.0.41:53;
            server 10.0.0.42:53;
        }
    
        server {
            listen 53 udp;
            proxy_pass         dns_havuzu;
            proxy_responses    1;
            proxy_timeout      2s;
        }
    }
    

    Bu katmanda Nginx paketin içeriğine bakmaz, yalnızca bağlantıyı taşır; Host başlığına göre yönlendirme ya da HTTP hata koduna göre devretme burada mümkün değildir. Veritabanı dengelemesinde kritik bir nokta daha var: birden fazla yazma yapabilen sunucuya trafik dağıtmak veri tutarsızlığı üretir. Tipik kurulum, tek bir yazma sunucusu (backup ile yedeklenmiş) ve ayrı bir salt-okunur havuzdur.

    Yapılandırmayı her değiştirdikten sonra devreye almadan önce sözdizimini doğrula:

    # Söz dizimi ve dosya yolları kontrolü
    sudo nginx -t
    
    # Kesintisiz yeniden yükleme: mevcut bağlantılar tamamlanır
    sudo systemctl reload nginx
    
    # Hangi upstream'e ne kadar süre harcandığını kayıtlara ekle
    # log_format ile: $upstream_addr $upstream_status $upstream_response_time
    tail -f /var/log/nginx/access.log
    

    Erişim kayıt biçimine $upstream_addr, $upstream_status ve $upstream_response_time değişkenlerini eklemek, "hangi arka uç yavaş" sorusunu tahminle değil veriyle cevaplamanı sağlar. Bu üç değişken olmadan yük dengeleyici kayıtları neredeyse hiçbir işe yaramaz.

    Sık Yapılan Hatalar#

    Tek Nginx, tek hata noktası. Yük dengeleyici koydun, arkasına üç sunucu dizdin ama dengeleyicinin kendisi tek makinede çalışıyor. O makine çökerse üç sağlıklı sunucun da erişilmez olur. Çözüm, iki Nginx düğümü arasında paylaşılan bir sanal IP kurmaktır; Keepalived ve VRRP ile failover yazısında bu tam olarak anlatılıyor.

    proxy_set_header Host unutmak. Bunu yazmazsan Nginx arka uca Host olarak upstream adını gönderir. Sanal konak (virtual host) yapılandırması olan arka uçlar isteği tanımaz ve varsayılan siteyi ya da 404 döner. En çok kafa karıştıran belirti, aynı yapılandırmanın bir sunucuda çalışıp diğerinde çalışmamasıdır.

    Havuza gerçekten farklı makineler koymamak. Aynı fiziksel sunucudaki üç konteynere dengeleme yapmak yükü dağıtır ama erişilebilirlik sağlamaz; makine kapanınca üçü birden gider. Ölçekleme ile dayanıklılığı ayırmak gerekir.

    Oturum sorununu ip_hash ile örtmek. Çalışır görünür, sonra kurumsal bir müşterinin 400 kullanıcısı tek IP'den geldiğinde o sunucu çöker. Oturumu ortak depoya taşımak kalıcı çözümdür.

    Sağlık kontrolü olduğunu sanmak. Açık kaynak Nginx'te check diye bir direktif yoktur; internette gördüğün check interval=3000 örnekleri üçüncü parti bir modülden (nginx_upstream_check_module) gelir ve standart pakette bulunmaz. nginx -t bunu "unknown directive" diye reddeder.

    Sıkça Sorulan Sorular#

    Nginx yük dengeleyici olarak ücretsiz mi#

    Evet, açık kaynak Nginx ile yük dengeleme yapmak tamamen ücretsizdir; upstream, least_conn, ip_hash, hash ve proxy_next_upstream gibi direktiflerin hepsi standart pakette gelir. Ücretli olan Nginx Plus sürümü aktif sağlık kontrolü, çerez tabanlı sticky oturum, canlı durum API'si ve havuzu yeniden başlatmadan değiştirme gibi ek özellikler sunar. Küçük ve orta ölçekli kurulumların büyük bölümü açık kaynak sürümle rahatça çalışır.

    Nginx mi HAProxy mi daha iyi yük dengeleyici#

    İkisi de üretimde kanıtlanmış araçlardır, seçim ihtiyaca göre değişir. Zaten Nginx'i web sunucusu olarak kullanıyorsan, yük dengelemeyi de aynı süreçte yapmak yapılandırmayı tek dosyada tutar ve statik dosya servisi, önbellekleme, TLS sonlandırmayı bir arada verir. HAProxy ise aktif sağlık kontrolü, çok daha ayrıntılı devretme kuralları ve çalışırken sunucu ekleyip çıkarmayı sağlayan runtime API'siyle saf dengeleme işinde daha zengindir. Yaygın bir kurulum ikisini birlikte kullanır.

    Sağlık kontrolü olmadan çöken sunucuyu Nginx nasıl anlar#

    Nginx pasif kontrol yapar: gerçek kullanıcı isteklerinin sonucuna bakar. max_fails ile belirlediğin sayıda istek fail_timeout penceresi içinde hata verirse sunucuyu geçici olarak havuzdan çıkarır ve süre dolunca tek bir deneme isteğiyle geri gelip gelmediğini kontrol eder. Bunun bedeli, arızayı ilk fark eden isteğin bir kullanıcıya ait olmasıdır; proxy_next_upstream ile o isteği başka sunucuya devrederek kullanıcıyı hatadan koruyabilirsin.

    Yük dengeleyici arkasında kullanıcının gerçek IP'sini nasıl görürüm#

    Yük dengeleyicide proxy_set_header X-Real-IP $remote_addr; ve proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; satırlarını ekle. Arka uç sunucuda ise realip modülünü etkinleştirip set_real_ip_from ile yalnızca yük dengeleyicinin iç IP'sini güvenilir olarak tanımla. Bu ikinci adım kritiktir: güvenilir kaynak listesini geniş tutarsan ziyaretçiler kendi IP'lerini istedikleri gibi gösterebilir.

    Nginx yapılandırmasını değiştirince site kesintiye uğrar mı#

    Hayır, doğru komutu kullanırsan uğramaz. Önce nginx -t ile söz dizimini doğrula, sonra systemctl reload nginx çalıştır. Yeniden yükleme sırasında Nginx yeni worker süreçlerini başlatır ve eskilerini mevcut isteklerini bitirene kadar çalışır durumda tutar; açık bağlantılar kopmaz. restart ise süreci tamamen durdurup yeniden başlattığı için kısa bir kesinti ve kopan bağlantılar üretir.

    Kaç sunucuyu tek Nginx ile dengeleyebilirim#

    Pratikte sınır sunucu sayısı değil, yük dengeleyicinin ağ ve CPU kapasitesidir. Arka uçtaki sunucu sayısı 3 de olsa 30 da olsa yapılandırma açısından fark etmez. Asıl darboğaz genellikle TLS el sıkışmaları ve ağ bant genişliğidir; ihtiyacını hesaplamak için bant genişliği hesaplayıcı aracını kullanabilirsin.

    Kapanış#

    Nginx'i yük dengeleyici olarak kurmak birkaç satırlık iş ama üretimde ayakta kalan bir kurulum birkaç detaya bağlı: havuzu zone ile paylaşılan belleğe almak, keepalive kullanırken proxy_http_version 1.1 ve boş Connection başlığını unutmamak, X-Forwarded-Proto göndererek yönlendirme döngülerinden kaçınmak ve kayıtlara $upstream_addr ile $upstream_response_time eklemek. Bir de en önemlisi: yük dengeleyicinin kendisini tek hata noktası olarak bırakmamak.

    Bu yapıyı kurmak için tam root erişimine ihtiyacın var; VDS ve sanal sunucu paketlerimizde Nginx'i istediğin gibi yapılandırabilir, arka uç havuzunu büyütmek istediğinde bulut sunucu tarafında hızlıca yeni düğüm ekleyebilirsin. Yapılandırmayı kendin sürdürmek istemiyorsan sunucu yönetimi hizmetimiz kurulum, sertifika yenileme ve izleme işini üstlenir; dış katmanda ek bir koruma istiyorsan DDoS koruma ve WAF sayfalarına göz atabilirsin.

    NginxYük DengelemeLinux

    Uygulamaya geçmeye hazır mısınız?

    NVMe SSD, ücretsiz SSL ve %99.9 uptime garantisiyle Clou.TR hosting ve sunucu çözümleriyle projenizi hayata geçirin.