Mert Özen Her Satırda Daha İleri
DevOps Web Server'lar ve Hosting — #03

Reverse Proxy Arkasında ASP.NET Core: Forwarded Headers, KnownIPNetworks ve Doğru Client IP

Mert Özen 8 Kas 2026 11 dk 5 görüntülenme
Reverse Proxy Arkasında ASP.NET Core: Forwarded Headers, KnownIPNetworks ve Doğru Client IP

Proxy arkasındaki uygulama client'ın IP'sini ve HTTPS'i ancak UseForwardedHeaders doğru kurulursa görür: hangi header'ların işleneceğini söylemek ve proxy'nin adresini KnownProxies ya da .NET 10'daki KnownIPNetworks'e eklemek gerekir. HTTPS redirection döngüsünü, ASPDEPR005 uyarısını, ForwardLimit ile sahte X-Forwarded-For'u, X-Forwarded-Prefix ile PathBase'i ve birden fazla instance'ta Data Protection key'lerini gerçek koşularla görüyoruz.

Sürüm notu: Bu yazıdaki örnekler .NET 10 (ASP.NET Core runtime 10.0.14), Nginx 1.30.6 ve curl 8.23 ile Docker 29 üzerinde denenmiştir.

Önce Tahmin Et

Bookstore.Api artık Nginx'in arkasında. Nginx 443'te TLS'i (Transport Layer Security) sonlandırıyor ve isteği Kestrel'e düz HTTP (Hypertext Transfer Protocol) ile iletiyor. Uygulamada ASP.NET Core şablonundan gelen iki satır var: app.UseHsts() ve app.UseHttpsRedirection(). Ortam değişkeni olarak ASPNETCORE_HTTPS_PORT=443 verilmiş. Kullanıcı tarayıcıda https://bookstore.test/api/books/1'i açıyor.

Tahmin et: kullanıcı kitabı görür mü? Nginx'te X-Forwarded-Proto header'ı zaten gönderiliyor; bu, uygulamanın isteğin HTTPS (HTTP Secure) olarak geldiğini bilmesine yeter mi? Ve aynı ortam değişkeni verilmeseydi ne olurdu?

Proxy Neyi Saklar, Hangi Header Neyi Taşır?

Serinin ilk yazısında gördüğün gibi, Nginx arkasındaki uygulama client'ı değil Nginx'i görür. Kaybolan dört bilgi var ve her biri için yerleşik bir header kullanılıyor:

HeaderTaşıdığı bilgiASP.NET Core'da düzelttiği değer
X-Forwarded-ForClient'ın IP (Internet Protocol) adresi ve yoldaki proxy'ler, soldan sağaHttpContext.Connection.RemoteIpAddress
X-Forwarded-ProtoClient'ın kullandığı scheme: http ya da httpsHttpContext.Request.Scheme
X-Forwarded-HostClient'ın gönderdiği orijinal HostHttpContext.Request.Host
X-Forwarded-PrefixProxy'nin kestiği base pathHttpContext.Request.PathBase

Bu header'ları okuyup HttpContext'i düzelten parça forwarded headers middleware'i (ForwardedHeadersMiddleware). Microsoft Learn'deki "Configure ASP.NET Core to work with proxy servers and load balancers" sayfasının en önemli cümlesi şu: middleware varsayılan olarak hiçbir header'ı işlemez (ForwardedHeaders.None), yalnız loopback adreslerinden (127.0.0.0/8 ve [::1]) gelen proxy'lere güvenir ve zincirde tek proxy olduğunu varsayar. Bu üç varsayılan, aşağıdaki deneylerin hepsini açıklıyor.

UseForwardedHeaders: Kurulum ve Middleware Sırası

Bookstore.Api'de forwarded headers ayarı configuration'a bağlı, deneylerde ortam değişkeniyle açıp kapatabilmek için:

string mode = builder.Configuration["Proxy:Mode"] ?? "none";
if (mode != "none")
{
    builder.Services.Configure<ForwardedHeadersOptions>(options =>
    {
        options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
            | ForwardedHeaders.XForwardedHost | ForwardedHeaders.XForwardedPrefix;
        foreach (string cidr in (builder.Configuration["Proxy:KnownNetworks"] ?? "").Split(';', StringSplitOptions.RemoveEmptyEntries))
        {
            options.KnownIPNetworks.Add(System.Net.IPNetwork.Parse(cidr));
        }
        if (int.TryParse(builder.Configuration["Proxy:ForwardLimit"], out int limit))
        {
            options.ForwardLimit = limit;
        }
    });
}

WebApplication app = builder.Build();

if (mode != "none")
{
    app.UseForwardedHeaders();
}
if (builder.Configuration["Https:Redirect"] == "true")
{
    app.UseHsts();
    app.UseHttpsRedirection();
}

Sıra önemli: UseForwardedHeaders, scheme'e, host'a ya da client IP'sine bakan her middleware'den önce gelmeli. HSTS (HTTP Strict Transport Security), HTTPS redirection, authentication, rate limiting, HTTP logging hepsi düzeltilmiş değerleri görmek zorunda. Dokümana göre exception handling gibi diagnostik middleware'lerden sonra olabilir, ama UseHsts'ten önce olmalı. Pipeline sırasının genel mantığı ASP.NET Core Web API (Application Programming Interface) serisinin "Middleware Pipeline" yazısında.

Nginx config'i serinin ilk yazısındakinin TLS'li hâli. Aynı API ayrıca /bookstore/ alt yolunda da yayınlanıyor; birazdan lazım olacak:

upstream bookstore_api {
    server 172.30.85.21:8080;
}

server {
    listen 443 ssl;
    server_name bookstore.test;
    ssl_certificate     /certs/bookstore.test.crt;
    ssl_certificate_key /certs/bookstore.test.key;

    location /api/ {
        proxy_pass http://bookstore_api;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
    }

    # aynı API'yi bir alt yol altında yayınla
    location /bookstore/ {
        proxy_pass http://bookstore_api/;
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
        proxy_set_header X-Forwarded-Prefix /bookstore;
    }
}

Üç koşu: middleware yok, middleware var ama varsayılan ayarla, middleware var ve Nginx'in adresi güvenilir listede. Nginx 172.30.85.10'da, client 172.30.85.50'de:

$ curl -s --cacert ca.crt https://bookstore.test/api/whoami | jq -c '{remoteIp,scheme,host,pathBase,path,xForwardedFor,xOriginalFor,link}'

# A: Proxy__Mode yok
{"remoteIp":"::ffff:172.30.85.10","scheme":"http","host":"bookstore.test","pathBase":"","path":"/api/whoami","xForwardedFor":"172.30.85.50","xOriginalFor":"","link":"http://bookstore.test/api/books/1"}

# B: Proxy__Mode=on, KnownIPNetworks boş (yalnız loopback)
{"remoteIp":"::ffff:172.30.85.10","scheme":"http","host":"bookstore.test","pathBase":"","path":"/api/whoami","xForwardedFor":"172.30.85.50","xOriginalFor":"","link":"http://bookstore.test/api/books/1"}

# C: Proxy__Mode=on, Proxy__KnownNetworks=172.30.85.10/32
{"remoteIp":"172.30.85.50","scheme":"https","host":"bookstore.test","pathBase":"","path":"/api/whoami","xForwardedFor":"","xOriginalFor":"[::ffff:172.30.85.10]:56566","link":"https://bookstore.test/api/books/1"}

B'deki sessizlik en çok vakit kaybettiren durum: middleware eklenmiş, kod doğru görünüyor, ama hiçbir şey değişmiyor. Sebebi yalnız Debug seviyesinde log'lanıyor:

dbug: Microsoft.AspNetCore.HttpOverrides.ForwardedHeadersMiddleware[1]
      Unknown proxy: [::ffff:172.30.85.10]:56560

Nginx aynı makinede 127.0.0.1'den bağlansaydı varsayılan ayar yeterdi. Container'da, ayrı bir VM'de (virtual machine) ya da bir load balancer arkasında proxy'nin adresi loopback değil ve middleware ondan gelen header'ları görmezden geliyor. C'de Nginx güvenilir listeye girince her şey yerine oturuyor: RemoteIpAddress client'ın adresi, scheme https, link doğru. Middleware kullandığı değeri X-Forwarded-For'dan siliyor ve eski değeri X-Original-For'a yazıyor; header'ları sonradan log'larken bunu bil.

KnownProxies ve .NET 10'da KnownIPNetworks

Güvenilir proxy'leri tanımlamanın iki yolu var: KnownProxies tek tek IP adresi alıyor, KnownIPNetworks CIDR (Classless Inter-Domain Routing) bloğu. .NET 10'dan önce ikincisinin adı KnownNetworks'tü ve Microsoft.AspNetCore.HttpOverrides.IPNetwork tipini kullanıyordu. .NET'in kendisine System.Net.IPNetwork geldikten sonra ASP.NET Core'daki kopya obsolete oldu. Eski kodu .NET 10 ile derleyince:

// .NET 10 öncesi kullanım
options.KnownNetworks.Add(new Microsoft.AspNetCore.HttpOverrides.IPNetwork(IPAddress.Parse("10.0.0.0"), 8));
Program.cs(9,5): warning ASPDEPR005: 'ForwardedHeadersOptions.KnownNetworks' is obsolete: 'Please use KnownIPNetworks instead. For more information, visit https://aka.ms/aspnet/deprecate/005.' (https://aka.ms/aspnet/deprecate/005)
Program.cs(9,35): warning ASPDEPR005: 'IPNetwork' is obsolete: 'Please use System.Net.IPNetwork instead. For more information, visit https://aka.ms/aspnet/deprecate/005.' (https://aka.ms/aspnet/deprecate/005)

Uyarı, yani kod çalışmaya devam ediyor; breaking change dokümanı bunu "source compatibility" etkisi olarak sınıflandırıyor. Yeni hâli tek satır: options.KnownIPNetworks.Add(System.Net.IPNetwork.Parse("10.0.0.0/8")). Dosyada hem System.Net hem Microsoft.AspNetCore.HttpOverrides namespace'leri açıksa IPNetwork adı iki tipe birden uyar; Bookstore.Api'de bu yüzden tam adı yazdım.

Dokümanda dual-mode socket'lerde IPv4 adreslerinin ::ffff:10.0.0.1 gibi IPv6 biçiminde geldiği ve listeye bu biçimde eklenmesi gerekebileceği yazıyor. C koşusunda RemoteIpAddress ::ffff:172.30.85.10 olarak geldiği hâlde 172.30.85.10/32 eşleşti; .NET 10'da IPv4 CIDR'ı yeterli oldu. Kendi ortamında "Unknown proxy" log'unu görürsen ilk bakacağın yer yine bu biçim farkı.

Bir de servis sürümüyle gelen bir değişiklik var: ASP.NET Core 8.0.17 ve 9.0.6'dan beri middleware, X-Forwarded-For işlenmese bile bilinmeyen proxy'den gelen X-Forwarded-Proto, X-Forwarded-Host ve X-Forwarded-Prefix'i de yok sayıyor. Daha önce "proxy listesi boş ama scheme düzgün geliyordu" diyen bir uygulama, bu güncellemeden sonra B koşusundaki duruma düştü ve bazı ekipler bunu HTTPS redirection döngüsü olarak gördü.

HTTPS Redirection Döngüsü ve HSTS

Şimdi baştaki soruya dönelim. Middleware yok, UseHttpsRedirection var ve ASPNETCORE_HTTPS_PORT=443:

$ curl -sS -L -o /dev/null --max-redirs 5 -w "%{http_code} redirects=%{num_redirects}\n" https://bookstore.test/api/books/1
307 redirects=5
curl: (47) Maximum (5) redirects followed

$ curl -s -D - -o /dev/null https://bookstore.test/api/books/1
HTTP/1.1 307 Temporary Redirect
Server: nginx/1.30.6
Date: Sun, 08 Nov 2026 03:45:07 GMT
Content-Length: 0
Connection: keep-alive
Location: https://bookstore.test/api/books/1

Kullanıcı HTTPS ile geliyor, Nginx TLS'i bitirip Kestrel'e HTTP ile iletiyor, uygulama Request.Scheme'e bakıp "bu istek HTTP" diyor ve aynı adresin HTTPS hâline yönlendiriyor. Tarayıcı birkaç turdan sonra "too many redirects" hatası veriyor. X-Forwarded-Proto: https header'ı her istekte oradaydı; okunmadı.

Sorunun ikinci kısmı daha sinsi. ASPNETCORE_HTTPS_PORT verilmezse ve Kestrel'in kendi HTTPS endpoint'i yoksa middleware hangi porta yönlendireceğini bilemiyor:

$ curl -s -o /dev/null -w "%{http_code}\n" https://bookstore.test/api/books/1
200

warn: Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware[3]
      Failed to determine the https port for redirect.

Bu kez döngü yok, ama redirection da yok: biri Nginx'i atlayıp Kestrel'e HTTP ile gelse uygulama onu HTTPS'e taşımayacak. "Çalışıyor" görünen bu yapılandırmada güvenlik ayarı fiilen kapalı. Middleware'i açıp Nginx'i güvenilir listeye koyunca:

$ curl -s -D - https://bookstore.test/api/books/1
HTTP/1.1 200 OK
Server: nginx/1.30.6
Date: Sun, 08 Nov 2026 03:45:14 GMT
Content-Type: application/json; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Strict-Transport-Security: max-age=2592000

{"id":1,"title":"Dune","author":"Frank Herbert","price":12.50}

Döngü bitti ve bir header kazandık: UseHsts yalnız HTTPS isteklere Strict-Transport-Security ekliyor ve artık isteğin HTTPS olduğunu biliyor. max-age=2592000, ASP.NET Core'un varsayılan 30 günü. HSTS'i uygulamada mı Nginx'te mi vereceğin, ikisinin birden vermemesi gerektiği ve diğer güvenlik header'ları yedinci yazıda; HSTS'in kendisi ASP.NET Core Web API serisinin "CORS (Cross-Origin Resource Sharing) ve Güvenlik: HTTPS, HSTS, Header'lar ve Secret'lar" yazısında.

Uygulamanın kendisinin HTTPS'e yönlendirmesi, proxy arkasında tartışmalı bir karar. TLS Nginx'te bitiyorsa HTTP'den HTTPS'e yönlendirmeyi de Nginx'te yapmak (80 portunda tek satırlık bir return 301) daha basit ve uygulamanın scheme'i yanlış anlama riskini ortadan kaldırıyor. Microsoft'un Nginx dokümanı ise uygulamadaki middleware'i öneriyor. Hangisini seçersen seç, ikisini birden yapma ve forwarded headers'ı her durumda doğru kur: link üretimi, cookie'lerin Secure bayrağı ve OAuth (Open Authorization) yönlendirme adresleri de Request.Scheme'e bakıyor.

Header'lara Kimden Güvenmeli?

X-Forwarded-For düz bir HTTP header'ı; client istediğini yazabilir. Nginx'teki $proxy_add_x_forwarded_for, client'tan gelen değerin sonuna kendi gördüğü adresi ekliyor. Client X-Forwarded-For: 6.6.6.6 gönderirse Kestrel'e 6.6.6.6, 172.30.85.50 ulaşıyor. Middleware listeyi sağdan sola okuyor ve ForwardLimit kadar değer işliyor; her adımda sağdaki değeri bırakan adresin güvenilir olup olmadığına bakıyor. Üç koşu, hepsinde client sahte bir adres gönderiyor:

$ curl -s -H "X-Forwarded-For: 6.6.6.6" https://bookstore.test/api/whoami | jq -c '{remoteIp,scheme,xForwardedFor,xOriginalFor}'

# F-1: KnownNetworks=172.30.85.0/24 (client da bu ağda), ForwardLimit=2
{"remoteIp":"6.6.6.6","scheme":"https","xForwardedFor":"","xOriginalFor":"[::ffff:172.30.85.10]:51284"}

# F-2: KnownNetworks=172.30.85.10/32, ForwardLimit varsayılan (1)
{"remoteIp":"172.30.85.50","scheme":"https","xForwardedFor":"6.6.6.6","xOriginalFor":"[::ffff:172.30.85.10]:51292"}

# F-2b: KnownNetworks=172.30.85.10/32, ForwardLimit=2
{"remoteIp":"172.30.85.50","scheme":"https","xForwardedFor":"6.6.6.6","xOriginalFor":"[::ffff:172.30.85.10]:51302"}

F-1'de saldırgan istediği IP'yle görünüyor. İki hata bir araya geldi: güvenilir ağ client'ları da kapsayacak kadar geniş, ve ForwardLimit=2 middleware'e ikinci değere de bakma izni veriyor. Middleware sağdaki 172.30.85.50'yi "bu da güvenilir bir proxy" sanıp bir adım daha sola gidiyor ve 6.6.6.6'yı kabul ediyor. F-2b, limit 2 olsa bile güvenilir listenin dar tutulmasının yettiğini gösteriyor: 172.30.85.50 güvenilir olmadığı için middleware orada duruyor ve sahte değeri X-Forwarded-For'da bırakıyor.

Kural basit: güvenilir liste yalnız gerçekten senin proxy'lerini içersin ve ForwardLimit zincirdeki proxy sayısına eşit olsun. Önde CDN (Content Delivery Network), arkasında cloud load balancer, onun arkasında Nginx varsa üç katman var; her birinin adres aralığını bilmen gerekiyor.

Dokümanda kısa yol olarak geçen ASPNETCORE_FORWARDEDHEADERS_ENABLED=true ortam değişkeni, middleware'i XForwardedFor | XForwardedProto ile otomatik ekliyor ve güvenilir listeleri temizliyor. Yani herkese güveniyor. Nginx'i atlayıp doğrudan Kestrel'e gelen bir client:

$ curl -s -H "X-Forwarded-For: 6.6.6.6" -H "X-Forwarded-Proto: https" http://172.30.85.21:8080/api/whoami | jq -c '{remoteIp,scheme,xForwardedFor,xOriginalFor}'
{"remoteIp":"6.6.6.6","scheme":"https","xForwardedFor":"","xOriginalFor":"[::ffff:172.30.85.50]:42474"}

Düz HTTP ile gelen istek uygulamaya HTTPS gibi görünüyor ve IP'si sahte. Doküman bu değişkenin bulut ortamları için tasarlanmış ayarlar kullandığını ve KnownProxies gibi kısıtları açmadığını açıkça uyarıyor. Kestrel'e yalnız platformun kendi load balancer'ı ulaşabiliyorsa işe yarar; port başka bir yerden erişilebiliyorsa kullanma. IP'ye göre rate limiting, IP allowlist'i ya da audit log'u bu değere dayanıyorsa sahte header doğrudan güvenlik açığına dönüşüyor; Web Güvenliği serisinin "OWASP (Open Worldwide Application Security Project) A02: Security Misconfiguration" yazısında anlatılan yanlış yapılandırmanın tipik bir örneği.

Bir de Unix socket durumu var. Önceki yazıda gördüğün gibi socket üzerinden gelen istekte RemoteIpAddress null. Bu durumda middleware'i KnownIPNetworks vermeden açtım:

{"remoteIp":"172.30.85.50","scheme":"http","xForwardedFor":"","xOriginalFor":""}

Header'lar işlendi; kontrol edilecek bir IP olmadığı için middleware proxy'yi reddetmedi. Unix socket'te güven sınırı IP listesi değil, socket dosyasının izinleri: socket'e yazabilen her process header'ları uydurabilir.

Sub-Path: X-Forwarded-Prefix ve PathBase

Bookstore'un API'si bazen kendi domain'inde değil, https://bookstore.test/bookstore/ gibi bir alt yolda yayınlanır. Nginx proxy_pass http://bookstore_api/; satırındaki sondaki slash yüzünden /bookstore/ önekini kesip Kestrel'e /api/whoami gönderiyor. Uygulama routing için doğru path'i görüyor, ama ürettiği link'lerde önek yok. X-Forwarded-Prefix ve ForwardedHeaders.XForwardedPrefix bunu düzeltiyor:

$ curl -s https://bookstore.test/bookstore/api/whoami | jq -c '{remoteIp,scheme,host,pathBase,path,xForwardedFor,xOriginalFor,link}'
{"remoteIp":"172.30.85.50","scheme":"https","host":"bookstore.test","pathBase":"/bookstore","path":"/api/whoami","xForwardedFor":"","xOriginalFor":"[::ffff:172.30.85.10]:56576","link":"https://bookstore.test/bookstore/api/books/1"}

PathBase /bookstore, Path /api/whoami; link önekiyle birlikte üretiliyor. Proxy öneki kesmiyorsa, yani Kestrel'e /bookstore/api/whoami ulaşıyorsa, doğru araç app.UsePathBase("/bookstore"); UseRouting'den önce çağrılması gerekiyor. proxy_pass'teki sondaki slash'in tam olarak ne yaptığı bir sonraki yazıda.

Birden Fazla Instance: Data Protection Key'leri

Proxy arkasına ikinci bir Kestrel instance'ı koyduğun an forwarded headers'tan bağımsız bir sorun başlıyor. ASP.NET Core'un Data Protection sistemi cookie authentication'ı, antiforgery token'larını ve TempData'yı şifreliyor; key'ler varsayılan olarak her instance'ın kendi diskinde (container'da /root/.aspnet/DataProtection-Keys, log'daki uyarının dediği gibi) duruyor. Bir instance'ın şifrelediğini diğeri çözemiyor:

token: CfDJ8PGMJrZO0I9OvX1xWpP7Hr5nam2dvIr_Q92x...
api1: "cart-42"
api2: {"type":"https://tools.ietf.org/html/rfc9110#section-15.6.1","title":"An error occurred while processing your request.","status":500,"detail":"The key {b6268cf1-d04e-4e8f-bd7d-715a93fb1ebe} was not found in the key ring. For more information go to https://aka.ms/aspnet/dataprotectionwarning"}

Kullanıcı açısından bu, load balancer onu diğer instance'a gönderdiği an oturumunun düşmesi ya da formun antiforgery hatasıyla reddedilmesi demek; üstelik rastgele. Çözüm key ring'i paylaşmak ve iki instance'a aynı application name'i vermek:

IDataProtectionBuilder dataProtection = builder.Services.AddDataProtection();
string? keysDir = builder.Configuration["DataProtection:KeysDirectory"];
if (keysDir is not null)
{
    dataProtection.SetApplicationName("Bookstore.Api")
        .PersistKeysToFileSystem(new DirectoryInfo(keysDir));
}
token: CfDJ8PVkjF3TFrtHjwYH7R3xaUy8qAYw6RU7G7g8...
api1: "cart-42"
api2: "cart-42"

Deneyde ortak klasör bir Docker volume'üydü; key'ler orada XML (Extensible Markup Language) dosyaları olarak duruyor. Production'da ortak bir dosya sistemi, Redis, veritabanı ya da Azure Blob Storage olabilir; key'lerin diskte şifresiz durmaması için ProtectKeysWith* ile şifrelemeyi de ekle. Log'daki "No XML encryptor configured" uyarısı tam bunu söylüyor. Sticky session ile aynı kullanıcıyı hep aynı instance'a göndermek bu sorunu gizler ama çözmez: instance yeniden başladığında key'ler yine gider.

Üretimde Dikkat

Proxy topolojisini configuration'a taşı, koda değil. Güvenilir ağlar ortama göre değişiyor: development'ta loopback, staging'de bir container ağı, production'da load balancer'ın alt ağı. Bookstore.Api'deki gibi Proxy:KnownNetworks anahtarı ortam başına ayrı değer almayı kolaylaştırıyor.

"Unknown proxy" mesajı Debug seviyesinde. Production'da göremeyeceğin bir log'a güvenmek yerine deploy sonrası kontrolüne bir istek ekle: proxy üzerinden /api/whoami benzeri bir endpoint'i çağır ve scheme ile remoteIp'nin beklediğin değerler olduğunu doğrula. Bu endpoint'i dışarıya açık bırakma; yalnız iç ağdan ya da yetkiyle erişilsin.

X-Forwarded-Host'u işliyorsan AllowedHosts listesini de doldur. Liste boşsa her host kabul ediliyor; host header'ından link üreten bir uygulamada bu, şifre sıfırlama e-postasına saldırganın domain'ini koydurmanın yolu. Proxy'nin kendisi de bilinmeyen host'ları reddetmeli; Nginx'te bunun yolu dördüncü yazıdaki default_server.

Sık Düşülen Tuzaklar

UseForwardedHeaders'ı ekleyip ForwardedHeaders değerini vermemek. Varsayılan None; middleware hiçbir şey yapmaz ve hata da vermez.

Proxy'nin adresini güvenilir listeye eklemeyi unutmak. Loopback dışındaki her proxy için gerekli; belirti, B koşusundaki gibi "her şey doğru ama hiçbir şey değişmiyor".

Güvenilir listeyi geniş tutmak ya da tamamen temizlemek. 0.0.0.0/0, client'ları da içeren bir alt ağ, KnownProxies.Clear() ya da ASPNETCORE_FORWARDEDHEADERS_ENABLED, F-1 ve F-3 koşularındaki sahte IP'ye kapıyı açıyor.

Middleware'i pipeline'da geç çağırmak. Authentication, HTTPS redirection ya da HTTP logging ondan önce çalışırsa düzeltilmemiş değerleri görür; log'larda client IP'si yerine proxy'nin adresi kalır.

Instance sayısını artırırken Data Protection'ı unutmak. Tek instance'ta hiç görünmeyen hata, ikinci instance'la rastgele oturum düşmesi olarak ortaya çıkıyor.

Küçük Bir Deneme

★☆☆ C koşusunu Proxy__KnownNetworks yerine KnownProxies ile tekrarla: koda options.KnownProxies.Add(IPAddress.Parse("172.30.85.10")) ekle. Çalışıyor mu? Sonra adresi ::ffff:172.30.85.10 biçiminde ver ve farkı gözle.

★★☆ Nginx'te proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; satırını $remote_addr ile değiştir ve F-1 koşusunu tekrarla. Sahte IP hâlâ geçiyor mu? Bu değişiklik zincirde birden çok proxy olduğunda neyi bozar?

★★★ İki Kestrel instance'ını Nginx'te round robin ile arkaya koy, uygulamaya cookie authentication ekle ve key'leri paylaşmadan giriş yap. Kaçıncı istekte oturumun düşüyor? Key'leri paylaştıktan sonra bir instance'ı silip yeniden oluştur; kullanıcı hâlâ girişli mi?

Uygulama artık proxy arkasında client'ı doğru görüyor. Sıra proxy'nin kendisinde: bir sonraki yazıda Nginx'i kuruyoruz. nginx.org deposu ile dağıtımın paketi arasındaki fark, master ve worker process'ler, nginx.conf'un yapısı, server ve location block'larının eşleşme sırası, root ile alias farkı ve proxy_pass'teki sondaki slash.