Geonode logo
Geonode Team

Geonode Team

Dikemas kini: 16 Oktober 2026

Diterbitkan: 16 Oktober 2026

Pengaturan Proxy HttpClient C#: Kredensial, Batas Waktu, dan Verifikasi

Panduan ini menjelaskan cara mengonfigurasi proxy HttpClient dalam C# pada .NET 8: `WebProxy` dengan `NetworkCredential`, pilihan antara `HttpClientHandler` dan `SocketsHttpHandler`, penggunaan ulang klien yang benar dengan `IHttpClientFactory`, batas waktu dan pembatalan, konfigurasi variabel lingkungan, daftar bypass, serta dua pengecualian yang sebenarnya akan Anda temui dalam produksi. Artikel ini diakhiri dengan program konsol lengkap yang membuka sesi residensial yang tetap aktif, memverifikasi alamat IP keluar, dan mencoba kembali dengan sesi baru jika terjadi kegagalan. Semua penjelasan di bawah ini ditujukan untuk .NET 8 dengan pernyataan tingkat atas, namun API-nya telah stabil sejak .NET Core 3.0 dan kode yang sama dapat dikompilasi pada .NET 6 dan 7.

Panduan ini menjelaskan cara mengonfigurasi proxy HttpClient dalam C# pada .NET 8: WebProxy dengan NetworkCredential, pilihan antara HttpClientHandler dan SocketsHttpHandler, penggunaan ulang klien yang benar dengan IHttpClientFactory, batas waktu dan pembatalan, konfigurasi variabel lingkungan, daftar bypass, serta dua pengecualian yang sebenarnya akan Anda temui dalam produksi. Artikel ini diakhiri dengan program konsol lengkap yang membuka sesi residensial yang tetap aktif, memverifikasi alamat IP keluar, dan mencoba kembali dengan sesi baru jika terjadi kegagalan.

Semua penjelasan di bawah ini ditujukan untuk .NET 8 dengan pernyataan tingkat atas, namun API-nya telah stabil sejak .NET Core 3.0 dan kode yang sama dapat dikompilasi pada .NET 6 dan 7.

WebProxy dan NetworkCredential

Blok pembangunnya adalah System.Net.WebProxy, yang didokumentasikan di learn.microsoft.com. Komponen ini menyimpan alamat proxy dan, melalui properti Credentials-nya, nama pengguna dan kata sandi yang dikirimkan oleh handler saat proxy meminta otentikasi dengan kode status 407.

using System.Net;

var proxy = new WebProxy("http://premium-residential.geonode.com:9000")
{
    Credentials = new NetworkCredential("USERNAME", "PASSWORD")
};

Skema alamatnya adalah http:// meskipun targetnya menggunakan HTTPS. Untuk URL https://, handler mengirimkan permintaan CONNECT ke proxy dan menjalankan TLS dengan target di dalam terowongan tersebut, sehingga lompatan proxy itu sendiri tetap berupa HTTP biasa dan validasi sertifikat berlaku pada target persis seperti halnya tanpa proxy.

Karena kredensial disimpan dalam NetworkCredential dan bukan di dalam URL, Anda tidak perlu melakukan pengkodean persen pada karakter khusus dalam kata sandi. Hal ini merupakan salah satu keunggulan praktis dibandingkan klien yang hanya menerima informasi pengguna (userinfo) di dalam URL proxy.

HttpClientHandler versus SocketsHttpHandler

Dua kelas handler ini mendukung penggunaan proxy. HttpClientHandler adalah API yang sudah lama ada; sejak .NET Core 2.1, kelas ini mendelegasikan fungsinya ke SocketsHttpHandler di semua platform, sehingga tidak ada perbedaan fungsional dalam hal dukungan proxy. Alasan menggunakan SocketsHttpHandler secara langsung adalah opsi tambahan yang disediakannya, dan salah satunya sangat penting untuk proxy.

// Option A: HttpClientHandler
var handlerA = new HttpClientHandler
{
    Proxy = proxy,
    UseProxy = true
};

// Option B: SocketsHttpHandler (preferred for new code)
var handlerB = new SocketsHttpHandler
{
    Proxy = proxy,
    UseProxy = true,
    PooledConnectionLifetime = TimeSpan.FromMinutes(2)
};

PooledConnectionLifetime adalah opsi tersebut. HttpClient mengelompokkan koneksi dan menggunakannya kembali di seluruh permintaan. Dengan proxy bergilir di port 9000, alamat IP keluar dipilih saat koneksi ke proxy terjalin; permintaan berikutnya pada koneksi yang sama dalam kelompok tersebut mungkin tetap menggunakan alamat IP keluar yang sama. Memberikan batas waktu pada koneksi yang digabungkan akan memaksa terjadinya koneksi ulang secara berkala, yang juga akan mendeteksi perubahan DNS untuk host proxy. Dua menit adalah nilai default yang wajar; persingkat durasi tersebut jika Anda ingin rotasi terjadi lebih sering daripada yang disediakan oleh pergantian koneksi alami.

Tabel berikut merangkum kapan setiap opsi cocok digunakan.

KebutuhanPenangananPengaturan
Konfigurasi proxy minimal, kode yang sudah adaHttpClientHandlerProxy, UseProxy = true
Penggunaan ulang koneksi dengan batas waktu dan IP yang bergantiSocketsHttpHandlerPooledConnectionLifetime
Batasi koneksi per host proxySocketsHttpHandlerMaxConnectionsPerServer
Nonaktifkan proxy untuk satu kliensalah satuUseProxy = false

Menggunakan Kembali Satu HttpClient

HttpClient dirancang untuk dibuat sekali saja dan digunakan kembali selama masa pakai aplikasi. Membuat instance baru untuk setiap permintaan akan menghabiskan soket saat beban tinggi, dan jika menggunakan proxy, hal ini juga akan membuang terowongan CONNECT yang telah dikumpulkan dalam pool—yang baru saja Anda bangun dengan mengorbankan latensi. Dalam aplikasi konsol atau worker, pertahankan satu instance statis.

var http = new HttpClient(handlerB)
{
    Timeout = TimeSpan.FromSeconds(30)
};

IHttpClientFactory di ASP.NET Core

Di ASP.NET Core, daftarkan klien bernama dan lampirkan handler melalui ConfigurePrimaryHttpMessageHandler. Factory ini mengelola masa pakai handler untuk Anda dan merotasi handler sesuai jadwal, yang mencegah masalah habisnya soket dan DNS yang kedaluwarsa.

// Program.cs
using System.Net;

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddHttpClient("geonode", client =>
{
    client.Timeout = TimeSpan.FromSeconds(30);
})
.ConfigurePrimaryHttpMessageHandler(() =>
{
    var proxy = new WebProxy("http://premium-residential.geonode.com:9000")
    {
        Credentials = new NetworkCredential(
            Environment.GetEnvironmentVariable("GEONODE_USER"),
            Environment.GetEnvironmentVariable("GEONODE_PASS"))
    };

    return new SocketsHttpHandler
    {
        Proxy = proxy,
        UseProxy = true,
        PooledConnectionLifetime = TimeSpan.FromMinutes(2)
    };
});

var app = builder.Build();

app.MapGet("/ip", async (IHttpClientFactory factory) =>
{
    var client = factory.CreateClient("geonode");
    return await client.GetStringAsync("https://api.ipify.org?format=json");
});

app.Run();

Suntikkan IHttpClientFactory di mana pun Anda membutuhkan klien tersebut dan panggil CreateClient("geonode"). HttpClient yang dikembalikan murah untuk dibuat karena handler yang mahal di bawahnya dibagikan.

Batas Waktu dan CancellationToken

HttpClient.Timeout adalah total waktu yang diperbolehkan untuk sebuah permintaan, termasuk proses koneksi ke proxy, proses handshake CONNECT, enkripsi TLS dengan server tujuan, dan pembacaan header respons. Nilai defaultnya adalah 100 detik, yang terlalu lama untuk pekerja scraper. Atur nilai ini di sisi klien, dan untuk kontrol yang lebih presisi, tentukan nilai CancellationToken per permintaan.

using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15));

try
{
    var body = await http.GetStringAsync("https://example.com/", cts.Token);
}
catch (TaskCanceledException ex) when (ex.InnerException is TimeoutException)
{
    // HttpClient.Timeout elapsed
}
catch (TaskCanceledException)
{
    // the CancellationToken was cancelled (our 15 s budget)
}

Koneksi melalui jaringan perumahan menambah latensi dibandingkan dengan koneksi langsung, jadi sediakan beberapa detik tambahan per permintaan. Waktu tunggu yang konsisten pada tahap yang sama di banyak permintaan biasanya menandakan adanya masalah jalur jaringan ke host proxy, bukan target yang lambat.

DefaultProxy dan variabel lingkungan

Jika Anda tidak menetapkan nilai Proxy pada handler, .NET akan menggunakan HttpClient.DefaultProxy. Pada .NET Core 3.0 dan versi yang lebih baru, properti statis tersebut diinisialisasi dari variabel lingkungan HTTP_PROXY, HTTPS_PROXY, dan NO_PROXY, dan URL tersebut mungkin berisi kredensial.

export HTTP_PROXY="http://USERNAME:PASSWORD@premium-residential.geonode.com:9000"
export HTTPS_PROXY="http://USERNAME:PASSWORD@premium-residential.geonode.com:9000"
export NO_PROXY="localhost,127.0.0.1,.internal"

Anda juga dapat menetapkan HttpClient.DefaultProxy dalam kode saat startup, yang memengaruhi setiap handler yang tidak meng-override Proxy. Hal ini berguna dalam kontainer, tetapi ini merupakan status global: klien pemeriksaan kesehatan atau panggilan ke API internal juga akan melewati proxy residensial kecuali NO_PROXY mencantumkan host-host tersebut. Untuk apa pun di luar skrip sekali pakai, penetapan WebProxy secara eksplisit pada handler yang diberi nama akan lebih jelas.

BypassList dan BypassProxyOnLocal

WebProxy memiliki mekanisme pengecualian tersendiri. BypassProxyOnLocal = true melewati proxy untuk host yang tidak memiliki titik dalam namanya (seperti localhost atau nama mesin tanpa tambahan). BypassList menggunakan ekspresi reguler yang dicocokkan dengan URI tujuan.

var proxy = new WebProxy("http://premium-residential.geonode.com:9000")
{
    Credentials = new NetworkCredential("USERNAME", "PASSWORD"),
    BypassProxyOnLocal = true,
    BypassList = new[] { @"^https?://(.*\.)?internal\.example(:\d+)?/" }
};

Ingatlah bahwa ini adalah pola ekspresi reguler, bukan pola glob; titik yang tidak di-escape akan cocok dengan karakter apa pun.

Menangani Kesalahan 407 dan Kesalahan Lainnya

Ada dua jenis kesalahan yang menjadi penyebab hampir semua tiket proxy HttpClient. Yang pertama adalah 407 Proxy Authentication Required, yang berarti proxy menolak kredensial sebelum meneruskan apa pun. Kode ini biasanya muncul sebagai HttpRequestException, dan tergantung pada versi .NET serta titik kegagalan, Anda mungkin malah mendapatkan respons dengan kode status HttpStatusCode.ProxyAuthenticationRequired. Periksa keduanya.

using System.Net;
using System.Net.Http;

try
{
    using var response = await http.GetAsync("https://example.com/");

    if (response.StatusCode == HttpStatusCode.ProxyAuthenticationRequired)
    {
        throw new InvalidOperationException("proxy rejected credentials (407)");
    }

    response.EnsureSuccessStatusCode();
    var html = await response.Content.ReadAsStringAsync();
}
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.ProxyAuthenticationRequired)
{
    // Also 407: fix the username grammar or password; retrying will not help.
}
catch (HttpRequestException ex)
{
    // Connection refused, DNS failure, TLS error, or EnsureSuccessStatusCode on 4xx/5xx.
    Console.Error.WriteLine($"request failed: {ex.Message} (status {ex.StatusCode})");
}

Yang kedua adalah TaskCanceledException untuk waktu tunggu (timeout), yang telah dibahas di atas. Aturan penting untuk keduanya: kode 407 merupakan kesalahan konfigurasi dan tidak boleh dicoba ulang; sedangkan waktu tunggu atau kode 5xx dari target bersifat sementara dan layak untuk dicoba ulang pada sesi baru.

Sesi yang Tetap Melalui Nama Pengguna

Port 9000 memberikan alamat IP keluar baru untuk setiap koneksi baru. Jika alur kerja memerlukan satu alamat IP untuk beberapa permintaan (login yang diikuti halaman data, atau hasil yang dipaginasi), gunakan port 10000 dan pilih sesi melalui nama pengguna. Sistem tata bahasa ini menambahkan sufiks ke nama pengguna dasar Anda: -country-xx untuk penargetan geografis, -session-XXXXXXXX dengan ID alfanumerik 8 karakter, dan secara opsional -lifetime-SECONDS dari 180 hingga 86400.

static string NewSessionId() =>
    Guid.NewGuid().ToString("N")[..8]; // 8 alphanumeric chars

static WebProxy StickyProxy(string baseUser, string pass, string country = "us", int lifetime = 600)
{
    var user = $"{baseUser}-country-{country}-session-{NewSessionId()}-lifetime-{lifetime}";
    return new WebProxy("http://premium-residential.geonode.com:10000")
    {
        Credentials = new NetworkCredential(user, pass)
    };
}

Setiap permintaan melalui klien yang dibangun di atas proxy ini menggunakan alamat IP keluar yang sama selama sepuluh menit. Untuk mengganti IP, buat handler baru dengan ID sesi baru. Karena sesi terikat pada kredensial, dua klien dengan nama pengguna yang sama akan menggunakan alamat IP keluar yang sama, bahkan di antara proses yang berbeda.

Memverifikasi alamat IP keluar

Jangan pernah berasumsi bahwa proxy HttpClient sedang digunakan; pastikan terlebih dahulu. Kirim permintaan https://api.ipify.org?format=json (atau https://httpbin.org/ip) melalui klien dan bandingkan dengan alamat IP Anda yang sebenarnya. Dengan sesi yang tetap (sticky session), kedua panggilan akan mengembalikan alamat IP yang sama; sedangkan dengan port 9000 dan koneksi baru, keduanya akan berbeda.

using System.Text.Json;

var json = await http.GetStringAsync("https://api.ipify.org?format=json");
var ip = JsonDocument.Parse(json).RootElement.GetProperty("ip").GetString();
Console.WriteLine($"exit IP: {ip}");

Contoh lengkap di konsol (.NET 8)

Program di bawah ini menggabungkan semua fitur: sesi tetap, batas waktu yang ditentukan, deteksi kode kesalahan 407, serta hingga tiga upaya dengan sesi baru saat terjadi kegagalan sementara. Buatlah dengan dotnet new console -n ProxyDemo, ganti Program.cs, atur GEONODE_USER dan GEONODE_PASS, lalu jalankan dotnet run.

// Program.cs  (.NET 8, top-level statements)
using System.Net;
using System.Text.Json;

var baseUser = Environment.GetEnvironmentVariable("GEONODE_USER")
    ?? throw new InvalidOperationException("GEONODE_USER not set");
var password = Environment.GetEnvironmentVariable("GEONODE_PASS")
    ?? throw new InvalidOperationException("GEONODE_PASS not set");

var target = args.Length > 0 ? args[0] : "https://api.ipify.org?format=json";

for (var attempt = 1; attempt <= 3; attempt++)
{
    var sessionId = Guid.NewGuid().ToString("N")[..8];
    var user = $"{baseUser}-country-us-session-{sessionId}-lifetime-600";

    var handler = new SocketsHttpHandler
    {
        Proxy = new WebProxy("http://premium-residential.geonode.com:10000")
        {
            Credentials = new NetworkCredential(user, password)
        },
        UseProxy = true,
        PooledConnectionLifetime = TimeSpan.FromMinutes(2)
    };

    using var http = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(30) };
    using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(20));

    try
    {
        // 1. Confirm the exit IP for this session.
        var ipJson = await http.GetStringAsync("https://api.ipify.org?format=json", cts.Token);
        var ip = JsonDocument.Parse(ipJson).RootElement.GetProperty("ip").GetString();
        Console.WriteLine($"[attempt {attempt}] session {sessionId} exit IP {ip}");

        // 2. Fetch the real target on the same IP.
        using var response = await http.GetAsync(target, cts.Token);

        if (response.StatusCode == HttpStatusCode.ProxyAuthenticationRequired)
        {
            Console.Error.WriteLine("407: proxy rejected credentials, not retrying");
            return;
        }

        if ((int)response.StatusCode is 403 or 429 or >= 500)
        {
            Console.Error.WriteLine($"[attempt {attempt}] got {(int)response.StatusCode}, rotating session");
            continue;
        }

        var body = await response.Content.ReadAsStringAsync(cts.Token);
        Console.WriteLine($"{(int)response.StatusCode} {target} ({body.Length} chars)");
        return;
    }
    catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.ProxyAuthenticationRequired)
    {
        Console.Error.WriteLine("407: proxy rejected credentials, not retrying");
        return;
    }
    catch (HttpRequestException ex)
    {
        Console.Error.WriteLine($"[attempt {attempt}] network error: {ex.Message}");
    }
    catch (TaskCanceledException)
    {
        Console.Error.WriteLine($"[attempt {attempt}] timed out");
    }

    await Task.Delay(TimeSpan.FromMilliseconds(500 * attempt));
}

Console.Error.WriteLine("gave up after 3 attempts");

Penanganan (handler) sengaja dibuat ulang pada setiap upaya: sesi yang melekat (sticky session) terkait dengan nama pengguna dalam kredensial, sehingga sesi baru memerlukan WebProxy yang baru. Dalam layanan yang berjalan dalam waktu lama, Anda sebaiknya menyimpan satu klien per ID sesi, bukan per permintaan.

Kesalahan umum

Membuat HttpClient baru untuk setiap permintaan

Setiap klien baru akan membuka terowongan CONNECT baru dan meninggalkan soket di TIME_WAIT. Gunakan kembali satu instance, atau gunakan IHttpClientFactory, dan biarkan PooledConnectionLifetime menangani rotasi.

Menggunakan alamat proxy https://

Lompatan proxy menggunakan HTTP biasa; tujuan HTTPS dikirim melalui terowongan CONNECT. Skema https:// pada alamat WebProxy menginstruksikan .NET untuk menegosiasikan TLS dengan proxy itu sendiri, yang akan gagal jika endpoint yang dituju mengharapkan HTTP biasa.

Mencoba Kembali Saat Mendapat Kode Kesalahan 407

Kode kesalahan 407 berarti proxy tidak menerima kredensial. Mencoba kembali dengan nama pengguna dan kata sandi yang sama hanya akan membuang-buang waktu. Segera hentikan upaya dan periksa format nama pengguna, terutama panjang ID sesi dan rentang masa berlaku.

Mengabaikan NO_PROXY dan BypassList

Jika proxy berasal dari DefaultProxy, setiap panggilan keluar dalam proses tersebut akan menggunakannya, termasuk pemeriksaan kesehatan dan API internal. Cantumkan host-host tersebut di NO_PROXY atau BypassList, atau konfigurasikan proxy hanya pada klien yang ditentukan namanya.

Langkah selanjutnya

Kode di atas dapat dijalankan tanpa perubahan pada proxy residensial Geonode: 2,5 juta alamat IP online di lebih dari 195 negara, HTTP, HTTPS, dan SOCKS5 pada satu host, serta sesi tetap hingga 24 jam yang dipilih melalui sintaks nama pengguna yang ditampilkan di sini. Penargetan berdasarkan negara, provinsi, kota, dan ASN menggunakan sufiks yang sama, sehingga mempersempit lokasi keluar hanya memerlukan perubahan string, bukan perubahan kode. Harga berlaku untuk pay-as-you-go tanpa kontrak, mulai dari 10 GB seharga $7,92 per bulan, dengan uji coba 3 hari seharga $5. Lihat proksi perumahan untuk detailnya, atau bandingkan tingkatan layanan di halaman harga. Untuk tahap selanjutnya, mulailah dengan kasus penggunaan web scraping.

Pertanyaan yang Sering Diajukan

Bagaimana cara mengatur proxy HttpClient dengan nama pengguna dan kata sandi di C#?

Buat objek WebProxy dengan alamat proxy, lalu atur properti Credentials-nya ke objek NetworkCredential yang berisi nama pengguna dan kata sandi. Tetapkan WebProxy tersebut ke properti Proxy dari HttpClientHandler atau SocketsHttpHandler, atur UseProxy menjadi true, dan serahkan handler ke konstruktor HttpClient. Handler tersebut akan mengirimkan kredensial saat proxy merespons dengan kode status 407.

Apakah sebaiknya menggunakan HttpClientHandler atau SocketsHttpHandler untuk proxy?

Pada .NET Core 2.1 dan versi selanjutnya, HttpClientHandler merupakan pembungkus tipis di atas SocketsHttpHandler, sehingga keduanya berfungsi. SocketsHttpHandler mengekspos PooledConnectionLifetime, yang memungkinkan Anda mendaur ulang koneksi yang disimpan dalam pool secara berkala. Hal ini penting dalam konteks proxy yang berganti-ganti, karena koneksi yang digunakan kembali akan mempertahankan alamat IP keluar yang sama hingga koneksi tersebut ditutup. Gunakan SocketsHttpHandler untuk kode baru.

Mengapa permintaan proxy HttpClient saya memicu pengecualian TaskCanceledException?

HttpClient melaporkan timeout sebagai TaskCanceledException, bukan sebagai pengecualian timeout khusus. Pengecualian ini dilemparkan ketika HttpClient.Timeout habis atau ketika CancellationToken yang Anda berikan dibatalkan. Pada .NET 5 dan versi selanjutnya, pengecualian dalam (inner exception) adalah TimeoutException jika penyebabnya adalah batas waktu klien, sehingga Anda dapat membedakan keduanya.

Apakah HttpClient membaca variabel lingkungan HTTP_PROXY dan HTTPS_PROXY?

Ya. Pada .NET Core 3.0 dan versi yang lebih baru, HttpClient.DefaultProxy diinisialisasi dari HTTP_PROXY, HTTPS_PROXY, dan NO_PROXY, dan penangan (handler) menggunakannya jika tidak ada Proxy yang ditetapkan secara eksplisit. Kredensial dapat disertakan dalam URL. Mengatur variabel lingkungan memang praktis di dalam kontainer, tetapi menetapkan WebProxy secara eksplisit dalam kode lebih mudah dipahami.

Bagaimana cara mempertahankan IP yang sama di beberapa permintaan proxy HttpClient?

Gunakan port tetap 10000 dan tambahkan sufiks sesi ke nama pengguna, misalnya USER-country-us-session-a1b2c3d4-lifetime-600. Semua permintaan yang dikirim dengan nama pengguna tersebut akan berbagi satu alamat IP keluar selama masa berlaku yang ditentukan dalam detik, antara 180 dan 86.400. Buat ID sesi baru berisikan 8 karakter jika Anda ingin mendapatkan alamat IP baru.

Batasan apa saja yang berlaku saat melakukan scraping melalui proxy HttpClient perumahan?

Bandwidth ditagih per gigabyte, jadi unduh hanya yang Anda butuhkan dan hindari gambar jika memungkinkan. Sesi tetap mempertahankan IP selama maksimal 24 jam dan dapat berakhir lebih awal jika perangkat offline, sehingga kode Anda harus menangani perubahan IP. Proksi tidak membebaskan Anda dari ketentuan layanan situs, arahan robot, atau batasan laju.