SSD Nodes Learn 🎉 VPS $5.50/aydan başlayan
Rehberler Matt ConnorYazan Matt Connor

Nginx Reverse Proxy Yapılandırması Nasıl Yapılır?

Nginx reverse proxy kurulumunda proxy_pass, websocket desteği ve gerekli header ayarlarını adım adım yapılandırın. Ubuntu 24.04 üzerinde hatasız bir kurulum için rehber.

Nginx reverse proxy yapılandırması ne işe yarar

Bir Nginx reverse proxy, 80 ve 443 numaralı portlara gelen istekleri karşılar, bunları yerel bir portta dinleme yapan ilgili uygulamaya iletir ve uygulamanın yanıtını tarayıcıya geri döndürür. Yapılandırma tek bir server bloğundan oluşur ve bu blok oldukça kısadır. Zorlukların neredeyse tamamı, uygulamaya gerçek istemcinin kim olduğunu ve hangi protokolü kullandığını bildiren beş veya altı satırda toplanır.

Aşağıdaki tüm işlemler, Ubuntu 24.04 üzerinde, dağıtımın kendi Nginx paketi kullanılarak sıfırdan oluşturulmuştur. Başlangıç noktası, halihazırda 127.0.0.1:3000 üzerinde yanıt veren bir uygulamadır. Henüz bir proxy seçimi yapmadıysanız, öncelikle Nginx'in Caddy ve Traefik ile karşılaştırması başlıklı yazıyı okumalısınız. Aşağıda, Nginx yanıtının satır satır nasıl göründüğü açıklanmıştır.

Bu yapılandırmaları kendi sunucunuzda çalıştırın. Her değişiklikten sonra yeniden yükleme yapmadan önce sudo nginx -t ile test edin ve çıktısını inceleyin.

Ubuntu üzerinde Nginx yapılandırma dosyalarının konumu

sudo apt update
sudo apt install -y nginx
ls -l /etc/nginx/sites-enabled/

Ana yapılandırma dosyası /etc/nginx/nginx.conf konumundadır. Bu dosya, genel ayarları bir http { } bloğu içerisinde tanımlar ve ardından /etc/nginx/conf.d/*.conf ile /etc/nginx/sites-enabled/* dizinlerini içeri aktarır. Ubuntu ve Debian sistemlerinde her site için /etc/nginx/sites-available/ dizininde ayrı bir dosya oluşturulur ve /etc/nginx/sites-enabled/ dizinine sembolik bir bağ (symlink) eklenerek site aktif hale getirilir. Sembolik bağın silinmesi siteyi devre dışı bırakır ancak yapılandırma dosyasını korur.

Daha sonra kullanılacak iki direktif yalnızca http bağlamında çalışır, server bloğu içerisinde kullanılamazlar: map ve upstream. Bu direktifleri /etc/nginx/conf.d/ altında kendi dosyalarına yerleştirin; çünkü bu dizin http seviyesinde içeri aktarılmaktadır.

Paket, default adında varsayılan olarak etkin bir site ile gelir. Bu site default_server olarak işaretlenmiştir; bu, Host başlığı yapılandırmanızdaki hiçbir server_name ile eşleşmeyen tüm isteklere bu sitenin yanıt vereceği anlamına gelir. Bu site etkin kaldığı sürece, tanımladığınız alan adlarıyla eşleşmeyen istekler uygulamanız yerine bu siteye yönlendirilir. Kendi siteniz çalışır duruma geldiğinde bu sembolik bağı kaldırın.

sudo rm /etc/nginx/sites-enabled/default
sudo nginx -t
sudo systemctl reload nginx

Tek bir uygulamayı proxy eden en küçük sunucu bloğu

server {
    listen 80;
    listen [::]:80;
    server_name app.example.com;

    location / {
        proxy_pass http://127.0.0.1:3000;
    }
}

Bunu /etc/nginx/sites-available/app.example.com olarak kaydedin, ardından etkinleştirin ve yükleyin.

sudo ln -s /etc/nginx/sites-available/app.example.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx
curl -sI -H 'Host: app.example.com' http://127.0.0.1/

listen 80; IPv4 adresini, listen [::]:80; ise IPv6 adresini bağlar. İkinci satırı çıkarırsanız, DNS (alan adı sistemi) sorgusu sunucunuz için bir AAAA kaydı döndüren bir ziyaretçi bağlantı reddi hatası alır; IPv4 kullanan herkes ise çalışan bir site görür. Alacağınız hata raporunda "bende çalışıyor" ifadesi yer alır.

server_name, tarayıcının gönderdiği Host başlığı ile eşleştirilir. Boşluklarla ayrılarak birden fazla isim listelenebilir. Hiçbir blok eşleşmezse, nginx default_server olan bloğu kullanır; paketlenmiş sitenin kaldırılmasının nedeni budur.

location /, istek yolunda bir önek eşleşmesidir ve / her yolu eşleştirir. proxy_pass, nginx'in bağlantı açtığı adrestir. Uygulamayı 127.0.0.1 adresine bağlı tutun, böylece tek giriş yolu nginx üzerinden olur. Uygulama bir container içinde çalışıyorsa, onu 3000:3000 olarak değil 127.0.0.1:3000:3000 olarak yayınlayın; çünkü Docker kendi kurallarını yazar ve portları doğrudan ufw'yi atlayarak yayınlar, bu nedenle çıplak bir yayınlanmış port, güvenlik duvarınız ne derse desin internetten erişilebilir durumdadır.

curl satırı, sunucunun kendisinden doğru Host başlığını gönderir; böylece DNS herhangi bir yeri işaret etmeden önce bloğu test edebilirsiniz.

Başka bir ayar yapılmadığında nginx'in upstream'e gönderdikleri

proxy_pass tek başına kullanıldığında uygulamanızdan dört şeyi gizler.

nginx varsayılan olarak arka uçla HTTP/1.0 üzerinden konuşur ve Connection: close gönderir; bu nedenle her istek yeni bir upstream bağlantısı açar ve protokol yükseltmesi mümkün olmaz.

Host başlığı, 127.0.0.1:3000 olan proxy_pass değerine yeniden yazılır. Host üzerinden mutlak bağlantılar oluşturan bir uygulama, artık sunucu dışındaki hiç kimsenin açamayacağı bağlantılar üretir.

Uygulamaya ulaşan bağlantı nginx üzerinden geldiği için uygulama, istemci adresi olarak 127.0.0.1 değerini görür. Bu durumda uygulama içindeki her günlük satırı ve her hız sınırlaması, ziyaretçi yerine proxy'yi kaydeder.

Uygulama, tarayıcının HTTPS kullandığını anlayamaz çünkü aldığı bağlantı, loopback adresi üzerinde düz bir HTTP bağlantısıdır.

Dört satırlık bir yapılandırma tüm bunları düzeltir.

Ayarlanması gereken dört başlık ve her birinin arka uçta sağladığı görünürlük

location / {
    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;
}

Host, ziyaretçinin girdiği alan adını taşır. $host, isteğin içindeki alan adının port bilgisi çıkarılmış ve küçük harfe dönüştürülmüş halidir. Bu başlığı ayarladığınızda uygulamanız doğru mutlak URL'leri oluşturur: giriş sonrası yönlendirme veya şifre sıfırlama e-postasındaki bağlantı gibi. Bu başlığı atladığınızda söz konusu URL'ler 127.0.0.1:3000 adresini işaret eder; bu nedenle giriş yapıldığında tarayıcı bağlantıyı reddeden bir adrese yönlendirilir. Uygulamanız servisi 8080 portundan sunduğunuz için port bilgisine de ihtiyaç duyuyorsa, istemcinin gönderdiği başlığın aynısı olan $http_host değerini kullanın.

X-Real-IP, tek bir değer taşır: $remote_addr, yani nginx'in bağlantıyı kabul ettiği adres. Uygulamalar bunu kendi erişim günlükleri ve hız sınırlama (rate limiting) işlemleri için okur.

X-Forwarded-For, bir liste taşır. $proxy_add_x_forwarded_for, istemcinin halihazırda başlığa eklediği değerlerin sonuna $remote_addr değerini ekler; böylece değer virgülle ayrılmış bir liste haline gelir ve nginx'in eklediği girdi en sonda yer alır. Bu detay, başlığın güvenilir olup olmadığını belirler: bir istemci istediği herhangi bir X-Forwarded-For değerini gönderebilir, bu nedenle ilk girdiyi okuyan bir uygulama herhangi bir adrese yönlendirilebilir. Nginx uç sunucu (edge server) konumundaysa, istemcinin gönderdiği sürümü göz ardı etmek için $remote_addr kullanın. Ön tarafta bir CDN veya başka bir proxy varsa, set_real_ip_from ve realip modülünden real_ip_header değerlerini kullanın; böylece $remote_addr gerçek istemci adresi haline gelir.

X-Forwarded-Proto, http veya https değerini taşır. Çerçeveler (frameworks), çerezleri Secure olarak işaretleyip işaretlemeyeceğine ve HTTPS'e yönlendirme yapılıp yapılmayacağına karar vermek için bu başlığı okur. TLS destekli bir sitede bu başlığı atlarsanız, HTTPS'i zorunlu kılacak şekilde yapılandırılmış bir uygulama http değerini görür, HTTPS adresine bir yönlendirme yanıtı döner, nginx üzerinden bir sonraki isteği alır, hala http değerini görür ve tekrar yönlendirme yapar. Tarayıcı bu döngüde pes eder ve ERR_TOO_MANY_REDIRECTS hatasını gösterir.

Bu dört satırı her location bloğunda tekrarlamak, ayarların zamanla birbirinden kopmasına neden olur. Bunları tek bir dosyaya koyun ve include komutuyla dahil edin.

# /etc/nginx/snippets/proxy-headers.conf
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;
location / {
    include snippets/proxy-headers.conf;
    proxy_pass http://127.0.0.1:3000;
}

Buradaki kalıtım bir tuzak barındırır. Bir location bloğu, kendi içinde hiçbir proxy_set_header yönergesi tanımlamadığı sürece server bloğundaki yönergeleri devralır. location bloğu içine tek bir proxy_set_header eklediğiniz anda, server seviyesinde tanımlanan tüm başlıklar o location için devre dışı kalır. Bu nedenle hepsini tek bir seviyede tutun veya proxy işlemi yapan her location bloğuna ilgili snippet'i include edin.

WebSocket uygulamam neden bağlanıp hemen ardından bağlantıyı kesiyor?

Varsayılan ayarlar yükseltme (upgrade) işlemine izin vermediği ve varsayılan okuma zaman aşımı boşta kalan tünelleri 60 saniye sonra kapattığı için bu durum yaşanır. Bir WebSocket, Upgrade: websocket ve Connection: Upgrade başlıklarını taşıyan bir HTTP isteği olarak başlar. Bunlar "hop-by-hop" başlıklardır; yani bir proxy'nin bunları iletmek yerine tüketmesi beklenir ve HTTP/1.0 sürümünde herhangi bir yükseltme mekanizması bulunmaz. Her iki başlığın da manuel olarak tekrar eklenmesi gerekir.

Eşleme (map), kendi dosyası içinde http bağlamına yerleştirilmelidir.

# /etc/nginx/conf.d/websocket.conf
map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      close;
}

Ardından konum (location) bloğu gelir.

location / {
    include snippets/proxy-headers.conf;
    proxy_pass http://127.0.0.1:3000;

    proxy_http_version 1.1;
    proxy_set_header Upgrade    $http_upgrade;
    proxy_set_header Connection $connection_upgrade;

    proxy_read_timeout 3600s;
    proxy_send_timeout 3600s;
}

Eşleme, tek bir konumun her iki trafik türüne de hizmet verebilmesi için kullanılır. Normal bir istekte $http_upgrade boş olduğundan, $connection_upgrade değeri close olur. Bir yükseltme isteğinde ise bu değer websocket olur, dolayısıyla yukarı akışa (upstream) gönderilen başlık Connection: upgrade değerini alır. proxy_set_header Connection "upgrade"; değerini doğrudan kodlamak, bu başlığın her düz sayfa isteğinde de gönderilmesine neden olur ve bazı arka uçlar (backend) bu tür isteklere 400 hatası ile yanıt verir.

"Yükleniyor ancak güncellemeyi durduruyor" şeklindeki raporların nedeni proxy_read_timeout ayarıdır. Bu ayar varsayılan olarak 60 saniyedir ve bağlantının toplam ömrünü değil, arka uçtan gelen iki okuma arasındaki süreyi ölçer. 60 saniye boyunca sessiz kalan bir WebSocket, nginx tarafından kapatılır ve tarayıcı konsolunda soketin 1006 koduyla kapandığı görülür. Dakikada bir kereden daha sık kendi "heartbeat" sinyalini gönderen uygulamalar bu durumu fark etmez. Göndermeyenler ise dakika dolduğunda bağlantıyı kaybeder. Canlı düzenleyiciler ve paneller bu sorunun ilk görüldüğü yerlerdir; HTTPS arkasındaki self-hosted bir n8n örneği buna yaygın bir örnektir.

proxy_pass içindeki bir eğik çizgi (trailing slash) URL'lerimi neden değiştiriyor?

Kural tek bir cümleden ibarettir. Eğer proxy_pass bir URI (uniform resource identifier) ile bitiyorsa, basit bir / olsa dahi, nginx istek yolunun location önekiyle eşleşen kısmını kaldırır ve yerine bu URI'yi yerleştirir. Eğer proxy_pass ana makine (host) ve port kısmında bitiyorsa, istek yolu hiçbir değişikliğe uğramadan iletilir.

location /app/ {
    proxy_pass http://127.0.0.1:3000/;
}

/app/status için yapılan bir istek, arka uç (backend) sistemine /status olarak ulaşır.

location /app/ {
    proxy_pass http://127.0.0.1:3000;
}

/app/status için yapılan bir istek, arka uç (backend) sistemine /app/status olarak ulaşır.

Hangi biçimi kullanacağınız uygulamaya bağlıdır. Temel yol (base-path) veya alt klasör ayarı olan bir uygulama, /app hakkında bilgilendirilmiş şekilde ikinci biçimi ister. Önekler hakkında hiçbir bilgisi olmayan bir uygulama ise ilk biçime ihtiyaç duyar. İlk biçimin hemen fark edilen bir maliyeti vardır: uygulamanın döndürdüğü HTML içeriği hala /static/main.css gibi mutlak yollar içerir; tarayıcı bunları site kök dizininden ister, hiçbir location eşleşmez ve sayfa stiller yüklenmeden görüntülenir. Tarayıcının ağ (network) sekmesi, bu varlık (asset) isteklerinin 404 hatasıyla döndüğünü gösterir. Çözüm, uygulamanın kendi temel yol ayarı veya aynı arka ucu işaret eden ikinci bir location /static/ bloğudur.

Regex tabanlı bir location, proxy_pass içinde bir URI taşıyamaz. sudo nginx -t yapılandırmayı reddeder ve nedenini şu şekilde belirtir: "proxy_pass" cannot have URI part in location given by regular expression, or inside named location, or inside "if" statement, or inside "limit_except" block.

Bu tür sorunların tamamı, her uygulama kendi app.example.com adına sahip olduğunda ve location / üzerinden proxy edildiğinde ortadan kalkar. Alt yollar (sub-paths), yalnızca DNS kayıtları ekleyemediğiniz durumlarda uğraşmaya değerdir.

Birden fazla arka uç (backend) tek bir isim altında nasıl toplanır?

Bir upstream bloğu ile. Bu blok http bağlamına aittir; bu nedenle aynı dosya içerisinde server bloğunun üzerine veya /etc/nginx/conf.d/ içerisine yazılmalıdır.

upstream app_backend {
    least_conn;
    server 127.0.0.1:3000 max_fails=3 fail_timeout=30s;
    server 127.0.0.1:3001 max_fails=3 fail_timeout=30s;
    keepalive 32;
}

Ardından location bloğu bu ismi referans alır: proxy_pass http://app_backend;.

Varsayılan yöntem round robin'dir. least_conn, her isteği en az aktif bağlantıya sahip olan arka uca gönderir; bu yöntem, süreleri değişken olan istekler için uygundur. ip_hash, bir istemci adresini belirli bir arka uca sabitler. Uygulama oturumları kendi belleğinde tutuyorsa ip_hash kullanmanız gerekir; aksi takdirde, round robin yöntemi nedeniyle istekler oturumu bilmeyen diğer sunucuya yönlendirildiğinde kullanıcıların oturumu rastgele sonlanır. Oturumları paylaşımlı bir depolama alanına taşımak daha doğru bir çözümdür.

max_fails=3 fail_timeout=30s, 30 saniye içinde üç başarısız deneme gerçekleştiğinde ilgili sunucunun 30 saniye boyunca devre dışı bırakılacağı anlamına gelir. Bloktaki tüm sunucular bu duruma düştüğünde, istemciler 502 hatası alır ve hata günlüğünde no live upstreams while connecting to upstream mesajı görünür.

keepalive 32, worker süreci başına 32 adede kadar boşta bağlantıyı açık tutar; bu da çoğu istekten bir TCP el sıkışma sürecini eksiltir. Bu özellik yalnızca proxy_http_version 1.1 ile çalışır ve yukarı yönde (upstream) Connection: close kullanılmadığı durumlarda geçerlidir. Eğer aynı location bloğu WebSocket eşlemesi de kullanıyorsa, boş durumu close yerine boş bir dizge olarak değiştirin; böylece normal istekler Connection başlığını taşımaz ve havuzdaki bağlantı yeniden kullanılabilir.

map $http_upgrade $connection_upgrade {
    default upgrade;
    ''      '';
}

upstream bloğu içindeki isimler, nginx başladığında çözümlenir. Arka ucunuz yeniden başlatıldığında yeni bir adres alan bir container ise, siz reload komutunu verene kadar nginx eski adresi kullanmaya devam eder. Bir Docker ağı içerisinde, gömülü resolver kullanarak çözümleme işlemini istek anına taşıyabilirsiniz.

resolver 127.0.0.11 valid=10s;
set $backend http://app:3000;
proxy_pass $backend;

Container'lar sıkça oluşturulup siliniyorsa ve nginx yapılandırmasını sürekli güncellemek zorunda kalıyorsanız, container etiketlerini okuyabilen bir proxy daha iyi bir araçtır. Birden fazla Docker Compose uygulaması önünde Traefik kullanımı, rotaları doğrudan container'ların kendisinden oluşturur.

Yüklemeler neden 413 Request Entity Too Large hatası veriyor?

client_max_body_size varsayılan olarak 1 megabayt değerindedir. Daha büyük bir istek gövdesi, uygulamanız herhangi bir veri görmeden önce nginx tarafından reddedilir ve hata günlüğüne client intended to send too large body kaydedilir. Bu değeri server bloğunda veya yüklemelerin gerçekleştiği location içinde artırın.

client_max_body_size 512m;

0 değeri kontrolü tamamen devre dışı bırakır. Uygulamanın da kendi sınırları vardır; bu değişiklikten sonra hala 413 hatası alıyorsanız sorun backend tarafındadır ve uygulamanın kendi yükleme ayarına bakılmalıdır.

Varsayılan olarak nginx, upstream bağlantısını açmadan önce tüm istek gövdesini okur ve büyük verileri önce diskteki geçici bir dosyaya yazar. Bu durum uygulamayı yavaş istemcilerden korur, çünkü backend yüklemeyi tam yerel hızda alır. Çok büyük yüklemeler için bunun yerine akış (streaming) yöntemini kullanabilirsiniz.

proxy_request_buffering off;

Bu durumda backend gövdeyi geldiği anda alır ve bunu işleyebilecek kapasitede olmalıdır. Ayrıca gövde zaten iletildiği için nginx'in isteği başka bir upstream üzerinde yeniden deneme yeteneği kaybolur.

Varsayılan olarak 60 saniye olan client_body_timeout, tüm yükleme süresine değil, gövdenin iki ardışık okuması arasındaki süreye uygulanır. Yavaş ama istikrarlı bir yükleme bu sürede kesilmez. Ancak duraksayan bir yükleme düşürülür.

Yanıt tamponlama ve canlı çıktıyı bozan ayar

proxy_buffering varsayılan olarak etkindir ve genellikle istenen davranış budur. nginx, uygulamanızdan gelen yanıtı uygulamanın yazabildiği hızda okur, tutar ve yavaş bir istemciye o istemcinin kendi hızında sunar. Uygulama çalışanı (worker), yavaş bir indirme işleminin tamamını beklemek yerine erkenden serbest kalır.

Bu durum, akış (streaming) yanıtlarını bozar. Server-sent events ve canlı günlük çıktıları, bir tampon dolana kadar okuyucuya hiçbir şey göstermez. Tamponlamayı yalnızca ilgili konumda kapatın.

proxy_buffering off;

Eğer uygulama üzerinde kontrolünüz varsa, daha iyi bir yöntem yalnızca akış yanıtlarında X-Accel-Buffering: no başlığını göndermektir. nginx bu başlığı yanıt bazında okur ve tamponlamayı yalnızca o yanıt için devre dışı bırakır; böylece normal sayfalar avantajını korumaya devam eder.

Hata günlüğünde upstream sent too big header while reading response header from upstream ifadesi yer aldığında, yanıt başlıkları tek bir tampona sığmamış demektir. proxy_buffer_size varsayılan olarak tek bir bellek sayfası boyutundadır (platforma bağlı olarak 4 veya 8 kilobayt); uzun çerezler veya büyük kimlik doğrulama başlıkları bu alanı aşar. Her iki değeri de artırın.

proxy_buffer_size 16k;
proxy_buffers 8 16k;

TLS bu yapılandırmanın neresinde yer alır?

Nginx katmanında, yukarıdaki her şeyin önünde yer alır. TLS (transport layer security), proxy üzerinde sonlandırılır ve Nginx'ten uygulamaya giden bağlantı, ağdaki başka hiçbir cihazın okuyamayacağı loopback adresi üzerinden düz HTTP olarak devam eder. Uygulama, ziyaretçinin HTTPS kullandığını dört başlıktan dördüncüsü olan X-Forwarded-Proto üzerinden öğrenir.

Sertifika yollarını elle yazmayın. DNS kaydını sunucuya yönlendirin, güvenlik duvarını açın ve Certbot'un bu sunucu bloğunu düzenlemesine izin verin: Certbot, ssl_certificate yollarıyla birlikte listen 443 ssl satırını ve 80 numaralı porttan yönlendirmeyi otomatik ekler. Nginx için Certbot ile Let's Encrypt sertifikası oluşturma rehberi, sertifika oluşturma ve yenileme zamanlayıcısı konularını kapsamaktadır.

sudo ufw allow 'Nginx Full'
sudo ufw status

Nginx Full, Nginx paketinin yüklediği bir uygulama profilidir ve 80 ile 443 numaralı portları birlikte açar. Tüm ziyaretçiler HTTPS'e yönlendirilmiş olsa dahi, HTTP-01 yenileme doğrulaması için 80 numaralı portun açık kalması zorunludur.

Yapılandırmayı test edin ve ardından yeniden yükleyin

sudo nginx -t
sudo systemctl reload nginx

nginx -t, dahil edilen her dosyayı ayrıştırır ve testi başarılı olarak raporlar ya da işlemin durduğu dosya ve satır numarasını yazdırır. Yeniden yükleme yapmadan önce bu çıktıyı okuyun. Hatalı bir yapılandırma ile yapılan yeniden yükleme uygulanmaz: nginx önceki yapılandırmayı sunmaya devam eder, bu nedenle siz değişiklik yaparken site çalışır durumda kalır ancak değişiklikleriniz sessizce etkisiz kalır. systemctl restart farklı ve daha kötü bir şekilde davranır; çünkü yeniden başlatma işlemi önce çalışan sunucuyu durdurur, bu nedenle bir yapılandırma hatası nginx'in tamamen çalışmamasına neden olur. Varsayılan olarak yeniden yükleme yapın ve yeniden başlatma işlemini yalnızca gerektiren nadir değişiklikler için saklayın.

sudo tail -f /var/log/nginx/error.log
sudo ss -lntp | grep -E ':(80|443|3000)'

ss satırı, her portu hangi sürecin tuttuğunu gösterir; böylece uygulamanın gerçekten proxy_pass tarafından işaret edilen yerde dinleme yaptığını doğrulayabilirsiniz.

Karşılaşacağınız gerçek hatalar

connect() failed (111: Connection refused) while connecting to upstream hatası içeren 502 Bad Gateway. proxy_pass adresinde dinleme yapan bir servis bulunmuyor. Uygulama durdurulmuş, başka bir porta bağlanmış veya ana makinenin erişemeyeceği bir container içi adrese bağlanmış olabilir.

no live upstreams while connecting to upstream hatası içeren 502. upstream bloğundaki tüm sunucular, max_fails tarafından şu anda başarısız olarak işaretlenmiş durumda. Backend servislerini onarın. fail_timeout süresi dolduğunda nginx tekrar deneme yapacaktır.

upstream timed out (110: Connection timed out) while reading response header from upstream hatası içeren 504 Gateway Time-out. Backend bağlantıyı kabul etti ancak proxy_read_timeout saniye boyunca hiçbir veri göndermedi. Zaman aşımı süresini artırmak, gerçekten yavaş çalışan bir rapor için doğru, ancak kilitlenmiş bir uygulama için yanlış bir yöntemdir.

Tüm yollar uygulamadan 404 dönüyor. Sondaki eğik çizgi (trailing slash) kuralı yolu yeniden yazmış olabilir. Uygulamanın günlük kayıtlarındaki yol ile talep ettiğiniz yolu karşılaştırın.

Farklı bir site yanıt veriyor. server_name, Host başlığı ile eşleşmiyor; bu nedenle istek default_server bloğuna düştü.

Sayfa yükleniyor ancak arayüz yaklaşık bir dakika sonra donuyor. Bu durum WebSocket ile ilgilidir: Upgrade yapılandırması eksik veya proxy_read_timeout değeri hala 60 saniye olarak ayarlanmış olabilir.

FAQ

Nginx, proxy_pass ekledikten sonra neden 502 Bad Gateway hatası veriyor?

Nginx, proxy_pass içindeki adrese bağlantı açamıyordur. /var/log/nginx/error.log konumundaki hata günlüğü (error log) nedeni belirtir: connect() failed (111: Connection refused) while connecting to upstream o noktada hiçbir servisin dinleme yapmadığı, no live upstreams ise upstream bloğundaki tüm sunucuların başarısız olarak işaretlendiği anlamına gelir. Hangi sürecin portu tuttuğunu ve hangi adrese bağlı olduğunu görmek için sudo ss -lntp | grep 3000 komutunu çalıştırın. Bir container-iç adrese veya yazdığınızdan farklı bir porta bağlı olan uygulama, bu hatayı sürekli verir.

Uygulamam Nginx arkasında neden yaklaşık bir dakika sonra bağlantıyı kesiyor?

Bağlantı bir WebSocket'tir ve proxy_read_timeout hala varsayılan 60 saniye değerindedir; bu değer, backend'den gelen iki okuma arasındaki boşluğu ölçer. Sessiz kalan bir soket Nginx tarafından kapatılır ve tarayıcı konsolu 1006 hata kodunu raporlar. proxy_http_version 1.1 değerini ayarlayın, Upgrade ve Connection başlıklarını bir map ile $http_upgrade üzerinden iletin ve proxy_read_timeout değerini 3600s gibi bir süreye yükseltin. Upgrade başlığı olmadan yükseltme (upgrade) işlemi gerçekleşmez, bu nedenle uygulama polling yöntemine döner veya canlı güncellemeleri göstermez.

proxy_pass içindeki sondaki eğik çizgi (trailing slash) önemli mi?

Evet, backend'in aldığı yolu değiştirir. location /app/ ve proxy_pass http://127.0.0.1:3000/ yapılandırmasında, /app/status için gelen bir istek backend'e /status olarak ulaşır; çünkü host ve porttan sonraki her URI, eşleşen location önekinin yerini alır. Sondaki eğik çizgiyi kaldırırsanız, aynı istek /app/status olarak ulaşır. Öneki kaldırmak genellikle uygulamanın kendi varlık (asset) bağlantılarını bozar; bu bağlantılar mutlak kalır ve site kök dizininde 404 hatası verir. Bu yüzden, temel yol (base-path) ayarı olan bir uygulama, yolu olduğu gibi ileten form ile daha iyi çalışır.

Uygulamam neden her ziyaretçinin IP adresini 127.0.0.1 olarak günlüğe kaydediyor?

Çünkü uygulamanın aldığı bağlantı gerçekten Nginx'ten, loopback adresi üzerinden gelmektedir. Ziyaretçinin adresi uygulamaya yalnızca sizin ayarladığınız bir başlık ile ulaşır: tek bir değer için proxy_set_header X-Real-IP $remote_addr;, eklenen zincir için proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;. Uygulamanın bu başlıkları güvenilir kabul edecek şekilde yapılandırılması gerekir. Bir istemcinin kendi X-Forwarded-For başlığını gönderebileceğini unutmayın; bu nedenle Nginx uç sunucu (edge server) olduğunda, başlığı eklemek yerine $remote_addr ile üzerine yazın.

Nginx ile uygulamam arasındaki bağlantıda TLS gerekli mi?

Uygulama aynı sunucuda çalışıyorsa ve 127.0.0.1 adresine bağlıysa gerekli değildir, çünkü bu trafik makineden dışarı çıkmaz. TLS sonlandırmasını Nginx'te yapın, proxy_pass bağlantısını loopback üzerinden düz HTTP olarak tutun ve uygulamanın ziyaretçinin HTTPS kullandığını bilmesi için X-Forwarded-Proto $scheme başlığını gönderin. Eğer backend kontrolünüz dışındaki bir ağda farklı bir sunucuda yer alıyorsa, bu bağlantı kendi korumasına ihtiyaç duyar; ya backend'e HTTPS ile bağlanılmalı ya da iki makine arasında özel bir tünel kurulmalıdır.