Rehber · Güncelleme 2026-11-14
C# HttpClient Proxy: WebProxy, Oturum ve Bağlantı Havuzu
C# HttpClient proxy ayarı: WebProxy ve kimlik bilgisi, DefaultProxy ve ortam değişkenleri, proxy neden canlı istemcide değişmez, bağlantı havuzu ve rotasyon.

C# HttpClient proxy ayarı: üç nesne, tek sıra
C# HttpClient proxy ayarı üç nesneyle kurulur ve sıra önemlidir: önce proxy adresini ve kimlik bilgisini taşıyan bir WebProxy, sonra bu proxy’yi kullanacak bir SocketsHttpHandler (ya da HttpClientHandler), en son handler’ı saran HttpClient. Microsoft belgesine göre handler’ın Proxy özelliği IWebProxy tipindedir ve yalnız UseProxy açıkken dikkate alınır; UseProxy’nin varsayılan değeri zaten true’dur, yani proxy vermek için ayrıca bir şey açmanız gerekmez.
En kısa kurulum şudur: var proxy = new WebProxy(“http://gateway-adresi:port”) { Credentials = new NetworkCredential(“kullanici”, “sifre”) }; var handler = new SocketsHttpHandler { Proxy = proxy }; var client = new HttpClient(handler); var html = await client.GetStringAsync(“https://hedef.example/sayfa”). WebProxy sınıfının belgesi kimlik bilgisini açıkça tanımlar: Credentials özelliği “proxy sunucusuna kimlik doğrulaması için gönderilecek bilgi”dir. Handler’ın kendi Credentials özelliği ise hedef sunucuya yönelik kimlik bilgisidir; gateway kullanıcı adını oraya yazarsanız proxy 407 döner, hedef de hiç görmediği bir kimlikle karşılaşır. En sık yapılan hata budur.
WebProxy kurucuları adresin yanında iki şey daha alır: yerel adresleri atlama bayrağı ve atlanacak adres listesi. WebProxy(“http://gateway:port”, true, new[] { “.sirket-ici.example” }) biçimi proxy’siz gitmesi gereken iç servisleri ayırmanın belgeli yoludur; BypassProxyOnLocal ve BypassList özellikleri aynı işi sonradan da yapar. Ürün gateway’i tek adres olduğu için proxy adresi hep aynıdır; rotasyonu adres değil gateway sağlar ve kullanıcı adındaki oturum parametresi davranışı belirler. Bu mantık rotating proxy gateway rehberinde anlatıldı; burada yalnız .NET tarafı var.
HttpClientHandler ile SocketsHttpHandler arasında proxy açısından fark yoktur; ikisinde de Proxy ve UseProxy özellikleri vardır ve .NET Core 2.1’den beri HttpClientHandler zaten altta SocketsHttpHandler kullanır. Bağlantı havuzu, bağlantı ömrü ve SOCKS gibi ayarlar yalnız SocketsHttpHandler’da doğrudan görünür; bu yazıdaki örnekler o yüzden SocketsHttpHandler üzerinden gider.
Kod proxy’si vermezseniz ne olur: DefaultProxy ve ortam değişkenleri
Handler’a Proxy vermediğinizde istek proxy’siz gitmez; HttpClient.DefaultProxy devreye girer. Belgeye göre bu özellik Windows’ta önce ortam değişkenlerini, onlar tanımlı değilse kullanıcının sistem proxy ayarını okur; macOS’ta aynı sırayla sistem ayarına düşer; Linux’ta ortam değişkeni yoksa hiçbir adresi proxy’lemeyen boş bir örnek oluşturur. Okunan değişkenler HTTP_PROXY, HTTPS_PROXY, ALL_PROXY ve NO_PROXY’dir; büyük-küçük harf ayrımı olan sistemlerde küçük harfli ad önce denenir.
Bu yüzden Windows’ta çalışan bir servis, siz hiç proxy yazmadığınız hâlde sistem ayarındaki kurumsal proxy’den geçebilir; Linux konteynerinde ise aynı kod doğrudan hedefe gider. Rotasyon beklediğiniz isteklerin bir kısmı kendi sunucu IP’nizden çıkıyorsa ilk bakılacak yer burasıdır. Proxy’yi tamamen kapatmak için handler’da UseProxy = false verilir; belge Proxy özelliğinin varsayılanını null olarak tanımlar ve null, “proxy yok” değil “varsayılanı kullan” demektir.
Ortam değişkeni biçimi belgeye göre kullanıcı adı ve şifre taşıyabilir: http://kullanici:sifre@gateway:port ve socks5://kullanici:sifre@gateway:port geçerlidir; socks4 ve socks4a yalnız kullanıcı adı alır, şifre yok sayılır. NO_PROXY’de joker karakter desteklenmez; alt alan adlarını kapsamak için değer baştaki noktayla yazılır (.example.com biçimi www.example.com ile eşleşir, example.com ile eşleşmez). Oturum parametresini kullanıcı adına gömdüğünüz kurulumlarda değişken yolu kullanışlıdır ama oturum kimliğini her çalışmada değiştirmek için süreci yeniden başlatmak gerekir; kodda WebProxy tanımlamak bu yüzden daha esnektir.
Proxy canlı istemcide neden değişmez?
HttpClient proxy değiştirme aramasının cevabı kısa: değiştiremezsiniz. SocketsHttpHandler ilk isteği gönderdikten sonra özelliklerini kilitler; Proxy, UseProxy ya da PooledConnectionLifetime gibi bir özelliğe sonradan değer atamak InvalidOperationException fırlatır. Bu bir tasarım kararıdır: handler bir bağlantı havuzu taşır ve havuzdaki bağlantılar belli bir proxy üzerinden açılmıştır. Proxy değişince eski bağlantıların anlamı kalmaz.
Rotating gateway ile bu kısıt çoğu zaman sorun çıkarmaz, çünkü proxy adresi sabittir; değişmesi gereken şey çıkış IP’sidir ve onu gateway değiştirir. Tek istemci, tek gateway, her istekte yeni IP: kurulumun tamamı budur. Sorun, aynı süreçte birden çok oturumu yan yana yürütmek istediğinizde başlar. Sticky oturumda kimlik gateway’in kullanıcı adında taşındığı için her oturum kendi WebProxy’sine, dolayısıyla kendi handler ve HttpClient örneğine ihtiyaç duyar. Microsoft’un HttpClient yönergesi bunu açıkça yazar: birden çok proxy senaryosunda birden çok istemci örneği gerekir.
İkinci yol özel bir IWebProxy yazmaktır. Arayüz üç üyeden oluşur: GetProxy(Uri) hedef adrese göre hangi proxy adresinin kullanılacağını döndürür, IsBypassed(Uri) proxy’siz gidecek adresleri ayırır, Credentials kimlik bilgisini verir. Handler her istekte GetProxy’yi çağırır; bu sayede tek handler ile istek başına farklı uç nokta seçebilirsiniz, örneğin port bazlı IPv6 ürününde on portu sırayla dolaşmak gibi. Farklı uç noktalara farklı kimlik gerekiyorsa Credentials özelliğinden bir CredentialCache döndürün; .NET kimliği proxy adresine göre oradan seçer.
Üçüncü yol, istemci havuzudur: oturum başına bir HttpClient tutan küçük bir sözlük. Her istemci kendi handler’ını ve kendi oturum parametreli WebProxy’sini taşır; oturum bitince istemci Dispose edilir ve sözlükten silinir. Bu düzen Python ve Node rehberlerindeki oturum nesnesi mantığının .NET karşılığıdır. Dikkat edilecek tek şey havuzun sınırsız büyümemesidir: oturum kimliği dışarıdan gelen bir değerden türetiliyorsa üst sınır koyun.
Bağlantı havuzu ve rotasyon: keep-alive aynı IP’yi geri getirir
SocketsHttpHandler açık bağlantıları hedef ve proxy çiftine göre havuzda tutar. HTTPS hedefte bu bağlantı gateway’e açılmış bir CONNECT tünelidir; tünel açık kaldığı sürece aynı hedefe giden sonraki istekler aynı tünelden, dolayısıyla aynı çıkış IP’sinden gider. İstek başına rotasyon beklerken art arda beş istekte aynı IP’yi görmenizin nedeni budur; gateway rotasyonu çalışıyordur, ama istemci ona yeni bir bağlantı açma fırsatı vermemiştir. Aynı belirtinin diğer nedenleri rotating proxy IP değişmiyor rehberinde sıralandı.
Havuzu yöneten ayar PooledConnectionLifetime’dır. Belgeye göre varsayılanı InfiniteTimeSpan’dir, yani bağlantı siz kapatmadıkça ya da karşı taraf düşürmedikçe havuzda kalır. Süre bağlantının kurulduğu andan sayılır, boşta ya da meşgul olması fark etmez ve aktif isteği olan bağlantı kesilmez; süre dolduğunda son isteği tamamlayıp kapanır, kuyrukta bekleyen varsa yenisi açılır. Belge bu ayarı DNS değişikliklerini yakalamak için önerir; rotasyon için de aynı mekanizma işe yarar: kısa bir ömür, tünelin düzenli aralıklarla yeniden kurulmasını ve çıkış IP’sinin yenilenmesini sağlar.
Her istekte yeni IP istiyorsanız iki seçenek daha vardır. Birincisi isteğe bağlantıyı kapatma işareti koymaktır: request.Headers.ConnectionClose = true; yanıt alındığında bağlantı havuza dönmez, sonraki istek yeni tünel açar. İkincisi, handler’ın MaxConnectionsPerServer değerini düşük tutup eşzamanlılığı tünel sayısıyla sınırlamaktır; bu IP çeşitliliğini artırmaz, yalnız aynı anda açık tünel sayısını kontrol eder. Her yeni tünelin bedeli bir CONNECT turu ve tünel içinde yeni bir TLS el sıkışmasıdır; istek başına rotasyonun gecikme maliyeti rotasyon aralığı rehberinde ölçüldü.
Tersini isteyen, yani aynı IP’de kalmak isteyen ekip keep-alive’a güvenmemelidir. Tünel her an düşebilir, havuz ömrü dolabilir, hedef bağlantıyı kapatabilir; o anda yeni tünel açılır ve IP değişir. Sabit IP’nin tek güvenilir yolu gateway’in oturum parametresidir; keep-alive yalnız gecikmeyi düşürür, kimliği korumaz.
IHttpClientFactory ile adlandırılmış istemciler: proxy başına bir ad
ASP.NET Core ya da Generic Host üzerinde çalışan uygulamalarda istemciler IHttpClientFactory ile kurulur. Proxy ayarı birincil handler’da yapılır: services.AddHttpClient(“rotating”).ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler { Proxy = new WebProxy(“http://gateway:port”) { Credentials = new NetworkCredential(“kullanici”, “sifre”) } }). Sonra factory.CreateClient(“rotating”) ile istemci alınır. Her proxy ya da oturum düzeni için ayrı ad tanımlanır; belge istemci adlarının sınırsız bir girdiden türetilmemesini söyler, çünkü her ad kendi handler’ını ve havuzunu taşır.
Factory’nin handler ömrü varsayılan olarak iki dakikadır ve SetHandlerLifetime ile değiştirilir. Süre dolunca handler geri dönüştürülür, yeni handler yeni havuzla başlar; yani her iki dakikada bir tüneller yeniden kurulur ve çıkış IP’si değişir. Rotating kullanımda bu istenen davranıştır. Oturum sürekliliğine keep-alive üzerinden güvenen bir kurulumda ise görünmez bir kopma noktasıdır; kimlik yine gateway oturum parametresinde taşınmalıdır.
Belgenin iki uyarısı proxy kullanımında da geçerlidir. Birincisi çerezler: factory handler’ları havuzladığı için CookieContainer paylaşılır, ilgisiz işler arasında çerez sızabilir ve handler ömrü dolunca çerezler kaybolur; oturum çerezine bağımlı işlerde factory yerine uzun ömürlü tek istemci önerilir. İkincisi sahiplik: factory’den alınan istemci kısa ömürlü tasarlanmıştır, alındıktan sonra factory’nin onun üzerinde kontrolü yoktur. Uzun süren bir tarama döngüsünde istemciyi döngü başında alıp sonunda bırakmak yeterlidir.
SOCKS5 ve .NET sürümü
SOCKS desteği HttpClient’a .NET 6 ile geldi; Microsoft’un sürüm yazısı SOCKS4, SOCKS4a ve SOCKS5’in eklendiğini ve kullanım için yalnız şemanın yettiğini yazar: Proxy = new WebProxy(“socks5://gateway:port”). Kimlik bilgisi yine Credentials özelliğine yazılır. Ortam değişkeni yolunda da socks5://kullanici:sifre@gateway:port biçimi geçerlidir. .NET Framework 4.x’te bu şema yoktur; orada HttpClient altta WebRequest kullanır ve SOCKS için üçüncü taraf bir kütüphane ya da yerel bir aktarıcı gerekir.
Ürün kataloğunda rotating IPv4 datacenter, rotating IPv4 residential ve mobile GB bazlı; IPv6 ürünleri port bazlı listelenir. Hangi ürünün hangi protokolü sunduğu panelde yazar; HTTPS hedefler için HTTP CONNECT ile SOCKS5 arasında pratik fark küçüktür, ikisi de tünel açar. SOCKS seçtiyseniz UDP beklemeyin; HttpClient yalnız TCP üzerinden HTTP konuşur.
Hata, neden, çözüm: .NET’te görülen istisnalar
407 Proxy Authentication Required iki biçimde görünür. HTTPS hedefte tünel isteği reddedildiği için HttpRequestException fırlatılır ve mesajda proxy tünel isteğinin 407 ile başarısız olduğu yazar; düz HTTP hedefte ise istisna yoktur, yanıtın StatusCode değeri ProxyAuthenticationRequired olur. İki durumda da neden aynıdır: kimlik bilgisi WebProxy.Credentials yerine handler.Credentials’a yazılmıştır, şifrede özel karakter vardır ya da kullanıcı adındaki oturum parametresi yanlış yazılmıştır. Ayrıntılı ayrıştırma 407 ve rotasyon hataları rehberinde var.
TaskCanceledException çoğunlukla zaman aşımıdır; HttpClient.Timeout varsayılan olarak 100 saniyedir ve dolduğunda iptal istisnası gelir. Rotating gateway arkasında bu kodun tipik nedeni hedefin yavaşlığı değil, eşzamanlı bağlantı sınırının aşılıp isteğin gateway’de kuyrukta beklemesidir; eşzamanlılığı düşürüp tekrar ölçün. İç istisnası SocketException olan HttpRequestException, gateway adresine TCP bağlantısının kurulamadığını söyler: adres, port, DNS ya da güvenlik duvarı.
Hedeften gelen 429 ve 403 istisna değildir; EnsureSuccessStatusCode çağırmadıysanız yanıt nesnesi normal döner. 429’da Retry-After başlığını okuyup beklemek ve aynı isteği yeni tünelle tekrarlamak retry ve backoff rehberindeki düzendir; 429 too many requests yazısı hangi 429’un proxy’den, hangisinin hedeften geldiğini ayırır. Teşhis sırası dört adımdır: proxy’siz tek istek, proxy’li tek istek bir IP yankı adresine, aynı istek hedefe, en son eşzamanlı yük.
Beş istekli rotasyon deneyi
Kurulumu doğrulamak için istemci IP’sini düz metin döndüren bir adrese beş istek atın. Birinci turda tek HttpClient ve varsayılan ayarlarla art arda beş GetStringAsync çağırın; büyük olasılıkla beş kez aynı IP görürsünüz, çünkü tünel yeniden kullanılmıştır. İkinci turda her isteğe ConnectionClose = true koyun ya da PooledConnectionLifetime’ı birkaç saniyeye çekip istekler arasında bekleyin; IP’ler değişmelidir. Üçüncü turda kullanıcı adına oturum parametresi ekleyip ConnectionClose ile beş istek atın; IP sabit kalmalıdır. Üç tur, üç farklı sonuç: rotasyonu gateway’in yaptığını, bağlantı havuzunun onu gizlediğini ve oturumun kimlikle korunduğunu tek deneyde görürsünüz.
Deneyi async/await ile paralel çalıştırırken Task.WhenAll içine beş isteği aynı anda koyun: havuzda tek bağlantı olduğu için SocketsHttpHandler beş ayrı tünel açar ve beş farklı IP görürsünüz; ardışık çalıştırmada ise tek tünel yeter ve IP tekrarlanır. Aynı kodun iki farklı sonucu, “rotasyon çalışmıyor” şikâyetlerinin önemli bir bölümünü açıklar.
Canlıya almadan önce kontrol listesi
Kimlik bilgisi WebProxy.Credentials’ta, handler.Credentials boş. UseProxy açık ve Linux’ta ortam değişkeni beklentisi doğru; proxy’siz gitmesi gereken iç servisler BypassList’te. HttpClient her istekte yeniden oluşturulmuyor; ya uzun ömürlü tek istemci ve PooledConnectionLifetime, ya IHttpClientFactory. Rotasyon modu bilinçli: her istekte yeni IP için ConnectionClose ya da kısa havuz ömrü, sabit IP için oturum parametresi.
Oturum sayısı kadar istemci var ve havuzun üst sınırı belli. Timeout iş tipine göre ayarlandı, 429 ve 407 ayrı ayrı ele alınıyor. SOCKS5 kullanılacaksa hedef çalışma zamanı .NET 6 ya da üstü. Beş istekli deney üç turda beklenen sonucu verdi ve sonuç kayıt altında.
Sık sorulan sorular
Her istek için yeni HttpClient oluşturmak doğru mu? Hayır. Microsoft belgesi her istemcinin kendi bağlantı havuzunu taşıdığını ve istek başına oluşturup bırakmanın port tükenmesine yol açtığını yazar; TCP portları bağlantı kapandıktan sonra hemen serbest kalmaz. Uzun ömürlü tek istemci ve PooledConnectionLifetime ya da IHttpClientFactory önerilir. Oturum başına istemci düzeni bunun istisnası değildir; oturum sayısı sınırlı ve istemci oturum boyunca canlıdır.
HttpClient sistem proxy’sini kullanıyor mu? Windows ve macOS’ta, kodda proxy vermediyseniz ve ortam değişkeni yoksa evet; Linux’ta hayır. Kodda Proxy verdiğinizde sistem ayarı devre dışı kalır. Hiç proxy istemiyorsanız UseProxy = false yazın.
.NET Framework 4.x’te SOCKS5 çalışır mı? HttpClient içinde hayır; destek .NET 6 ile geldi. Framework’te HTTP proxy kullanın ya da SOCKS gerekiyorsa yerel bir HTTP aktarıcı üzerinden geçin.
Proxy’yi yalnız bazı adreslerde atlamak mümkün mü? Evet; WebProxy kurucusundaki atlama listesi ve BypassProxyOnLocal bayrağı bunun içindir, IsBypassed(Uri) ile hangi adresin atlanacağını test edebilirsiniz. Ortam değişkeni yolunda NO_PROXY aynı işi görür, ama joker desteklemez.
Kimlik bilgisini proxy adresine gömebilir miyim? Ortam değişkeninde evet, belge http://kullanici:sifre@host:port biçimini tanımlar. Kodda WebProxy kullanırken belgeli yol Credentials özelliğidir; adresin içindeki kullanıcı bilgisine güvenmek yerine NetworkCredential verin.
