Acil tıpta ve anesteziyolojide kritik bir işlem vardır: Endotrakeal Entübasyon. Solunumu durmuş bir hastanın nefes borusuna (trakea) laringoskop yardımıyla plastik bir tüp yerleştirirsiniz. Bu tüpün ucu, trakeanın iki ana bronşa ayrıldığı anatomik çatala (karina) tam olarak 2-3 santimetre mesafede sabitlenmelidir. Eğer tüpü dikkatsizce sadece 1.5 santimetre daha derine iterseniz ne olur? Tüp anatomik açısı gereği doğrudan sağ ana bronşa kayar. Sonuç: Hastanın sağ akciğeri aşırı havalanıp yırtılma riskiyle (barotravma) karşı karşıya kalırken, sol akciğere tek bir molekül oksijen gitmez; sol akciğer tamamen söner (masif atelektazi) ve hasta parmaklarınızın ucunda morararak hipoksiye girer. Yalnızca tek bir santimetrelik hizalama hatası, hayat kurtaran bir boruyu ölümcül bir tıkaca dönüştürür.

Modern mikroservis ve web mimarisinde Nginx'in proxy_pass direktifinin sonuna konulan ya da unutulan o tek bir eğik çizgi (/), tam olarak bu endotrakeal tüpün ucundaki 1 santimetrelik kritik mesafedir.

Geliştirici Node.js, Python FastAPI, Go veya Java Spring Boot ile harika bir mikroservis yazar; uygulamanın önüne Nginx ters vekili (Reverse Proxy) koyar. Ancak o tek bir karakter yüzünden tüm URI yolları kayar, statik dosyalar kaybolur, API endpoint'leri 404 Not Found çöplüğüne döner ve saatlerce "Nginx çok karmaşık, bu web sunucusu çalışmıyor" diye feryat edilir.


O Meşhur Eğik Çizgi: URI Ekleme vs. Değiştirme Mantığı

Nginx belgelerinde açıkça yazan ama geliştiricilerin ezici çoğunluğunun okumadan geçtiği bir kural vardır: proxy_pass direktifine bir URI (yol) dahil edilmiş midir, edilmemiş midir?

Nginx bu konuda tavizsiz bir algoritmaya sahiptir. İki yapılandırma arasındaki farkı inceleyelim:

# SENARYO A: proxy_pass sonunda eğik çizgi YOK
location /api/ {
    proxy_pass http://127.0.0.1:8000;
}
 
# SENARYO B: proxy_pass sonunda eğik çizgi VAR
location /api/ {
    proxy_pass http://127.0.0.1:8000/;
}

İlk bakışta aradaki tek fark bir klavye vuruşudur. Oysa Nginx çekirdeği için bu iki satır bambaşka iki evrendir.


Mekanizmanın İç Organları: İstekler Nereye Gider?

Diyelim ki bir istemci web sitenize şu isteği attı:
GET /api/v1/users/list

Senaryo A (Eğik Çizgi Yok - Doğrudan İletim):

location /api/ {
    proxy_pass http://127.0.0.1:8000;
}

proxy_pass parametresinde port numarasından sonra hiçbir URI yolu (hatta tek bir / bile) belirtilmemiştir.

  • Nginx Kuralı: İstemciden gelen URI yoluna dokunma, aynen olduğu gibi arkadaki backend servisine fırlat.
  • Backend'e Ulaşan İstek: http://127.0.0.1:8000/api/v1/users/list
  • Eğer backend servisiniz (Express/FastAPI) rotalarını /api/v1/... şeklinde tanımladıysa işlem kusursuz çalışır.

Senaryo B (Eğik Çizgi Var - Normalleştirme ve Değiştirme):

location /api/ {
    proxy_pass http://127.0.0.1:8000/;
}

Burada http://127.0.0.1:8000/ yazılmıştır; yani porttan sonra bir URI kökü (/) tanımlanmıştır!

  • Nginx Kuralı: location eşleşmesindeki kısmı (/api/) istekten söküp at! Kalan kısmı proxy_pass direktifindeki URI'nin ucuna yapıştır!
  • İstemcinin İsteği: /api/v1/users/list
  • Sökülen Kısım: /api/
  • Kalan Kısım: v1/users/list
  • Backend'e Ulaşan İstek: http://127.0.0.1:8000/v1/users/list

Eğer backend uygulamanız /api önekini bekliyorsa, anında 404 Cannot GET /v1/users/list hatası patlar!

+------------------------------------------------------------------------+
|                   İSTEK: /api/v1/users/list                            |
|                                                                        |
|  [proxy_pass http://127.0.0.1:8000]                                    |
|  ===> Backend Alır: /api/v1/users/list (Birebir İletim)                |
|                                                                        |
|  [proxy_pass http://127.0.0.1:8000/]                                   |
|  ===> Backend Alır: /v1/users/list     (/api/ Söküldü, Değiştirildi)   |
+------------------------------------------------------------------------+

Daha da Beteri: Çift Eğik Çizgi ve Kelime Birleşmesi Felaketi

Geliştiricilerin en çok saç baş yolduğu varyasyon, location ile proxy_pass arasındaki eğik çizgi asimetrisidir.

Hata 1: Kelimelerin Birbirine Kaynaması

location /api {
    proxy_pass http://127.0.0.1:8000/v1;
}

İstemci istek atar: GET /api/users

  • Eşleşen kısım: /api
  • Kalan kısım: /users
  • Nginx birleştirir: http://127.0.0.1:8000/v1 + /users = http://127.0.0.1:8000/v1/users (Çalıştı gibi göründü).
  • Ama istemci GET /api isteği atarsa ne olur?
  • Kalan kısım: (boş)
  • Nginx birleştirir: http://127.0.0.1:8000/v1 -> Rota kaydı!

Hata 2: Çift Bölü (//) Çıkmazı

location /service/ {
    proxy_pass http://127.0.0.1:8000//;
}
# Veya
location /service {
    proxy_pass http://127.0.0.1:8000/;
}

İstemci istek atar: GET /service/profile

  • Eşleşen kısım: /service
  • Kalan kısım: /profile
  • Nginx birleştirir: http://127.0.0.1:8000/ + /profile = http://127.0.0.1:8000//profile
  • Birçok modern web framework'ü (Django, NestJS, Go Gin) çift bölü işaretini (//) geçersiz URI sayarak rotayı tanımaz ve 404 Not Found döner. Geliştirici loglara baktığında iki bölü işaretini yan yana göremez bile; göz yanılmasıyla günlerce kodda hata arar.

Regex (Düzenli İfade) Bloklarında Trajedi

Nginx mimarisinde en tehlikeli tuzaklardan biri, location bloğunda Regex (düzenli ifade) kullanırken proxy_pass sonuna yol eklemektir:

# BU YAPILANDIRMA SYNTAX ERROR VERİR VE NGİNX BAŞLAMAZ!
location ~* ^/api/(.*)$ {
    proxy_pass http://127.0.0.1:8000/$1;
}

Nginx konsolu acımasızca uyarır:
"proxy_pass" cannot have URI part in location given by regular expression, or inside named location, or in "if" statement...

Çünkü Nginx çekirdeği, regex ile ayrıştırılmış bir blokta URI'nin nasıl normalize edileceğini otomatik olarak belirleyemez. Burada değişkenler ($1) ve açık yeniden yazımlar (rewrite ... break) kullanılmak zorundadır:

# Doğru Regex Kullanımı:
location ~* ^/api/(.*)$ {
    rewrite ^/api/(.*)$ /$1 break;
    proxy_pass http://127.0.0.1:8000;
}

İstemci Bilgilerini Kaybetmemek: Hayati Proxy Başlıkları

proxy_pass doğru çalışsa bile, arkadaki backend servisi dünyayı sadece 127.0.0.1 olarak görmeye başlar. Kullanıcının gerçek IP'si, protokolü (HTTP mi HTTPS mi) ve orijinal Host adı buharlaşır.

Eksiksiz ve profesyonel bir ters vekil bloğu her zaman şu başlıkları taşımak zorundadır:

location /api/ {
    # Backend servisine birebir aktar
    proxy_pass http://127.0.0.1:8000;
 
    # Orijinal istemci üstbilgilerini koru
    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;
 
    # HTTP/1.1 ve Keep-Alive bağlantılarını aktif et (Performans için şart)
    proxy_http_version 1.1;
    proxy_set_header Connection "";
 
    # Zaman aşımı sürelerini ayarla
    proxy_connect_timeout 60s;
    proxy_send_timeout 60s;
    proxy_read_timeout 60s;
}

Sonuç

Mühendislikte "küçük detay" diye bir şey yoktur. Bir entübasyon tüpünün 1 santimetre derine inmesi akciğeri söndürür; Nginx yapılandırma satırındaki tek bir / karakteri ise tüm sistemi felç edip milyonlarca isteği karanlığa gömer.

Sisteminizin önüne bir ters vekil koyarken kopyala-yapıştır yapmayın. URI'nin nerede başlayıp nerede bittiğini, hangi önekin soyulup hangisinin aktarıldığını cerrahi bir dikkatle hesaplayın. Tek bir eğik çizginin arkasındaki mekanizmayı anladığınız gün, Nginx sizin için bir bulmaca olmaktan çıkıp güvenilir bir neşter haline gelir.