Retour au blog
Guides
Suciu DanLast updated on May 8, 202619 min read

Comment utiliser un proxy avec HttpClient en C#

Comment utiliser un proxy avec HttpClient en C#
En bref : pour utiliser un proxy avec HttpClient en C#, créez un WebProxy, associez-le à un HttpClientHandler (ou SocketsHttpHandler), puis transmettez ce gestionnaire au HttpClient . En production, remplacez les boucles manuelles par IHttpClientFactory, ajoutez NetworkCredential pour les proxys authentifiés, et encapsulez les appels dans des tentatives de réessai avec Polly afin que les adresses IP inactives ne mettent pas votre worker hors service.

Introduction

Si vous avez déjà essayé de scraper un site, d'accéder à une API verrouillée par région ou de tester la résistance d'un service à partir de plusieurs adresses IP de sortie, vous savez déjà pourquoi nous sommes ici. Ce guide explique comment utiliser un proxy avec HttpClient en C#, depuis une configuration de cinq lignes WebProxy à un pool de rotation qui ne fuit pas de sockets.

Un proxy HttpClient est simplement un HttpClient dont le gestionnaire est configuré avec une WebProxy, de sorte que les requêtes sortantes sont acheminées via une adresse IP intermédiaire au lieu d’aller directement à la cible. C’est là tout le principe de l’abstraction. Tout le reste – authentification, SOCKS5, validation SSL, rotation, tentatives de reconnexion – relève de la configuration autour de cette idée centrale.

Nous partons du principe que vous maîtrisez async/await et de la dotnet CLI sur une version récente de .NET. Nous ne supposerons pas que vous avez lu le code source de SocketsHttpHandler. À la fin, vous disposerez de modèles prêts à copier-coller pour les proxys non authentifiés, les proxys authentifiés, SOCKS5, la rotation avec IHttpClientFactory, la validation TLS sécurisée lorsqu’un proxy se trouve sur le chemin, ainsi qu’un tableau de dépannage pour les erreurs que vous rencontrerez inévitablement en production. Vous trouverez également à la fin une matrice de décision qui vous permettra de cesser de gérer votre propre pool lorsque cela ne vaudra plus la peine. Si vous souhaitez avoir une vue d’ensemble du scraping, notre guide d’introduction à la création d’un scraper web avec C# complète parfaitement celui-ci.

Un modèle mental pour l'utilisation d'un proxy avec HttpClient en C#

Avant tout code, veillez à bien définir les couches. HttpClient est un simple wrapper. Le transport proprement dit, y compris la résolution du proxy, repose sur son handler. Sur le .NET moderne, il s'agit soit de HttpClientHandler (la façade compatible avec les systèmes hérités) ou SocketsHttpHandler (le moteur sous-jacent). Les deux exposent une Proxy propriété de type IWebProxy, et l'implémentation intégrée est WebProxy.

Le flux se présente comme suit :

HttpClient
   |
   v
HttpMessageHandler  (HttpClientHandler / SocketsHttpHandler)
   |  Proxy = IWebProxy
   v
WebProxy  ->  proxy server  ->  upstream target

Cette stratification a deux conséquences. Premièrement, le proxy est lié au gestionnaire, et non au client. Vous ne pouvez pas modifier HttpClient.Proxy, car cette propriété n'existe pas. Si vous voulez un proxy différent, vous avez besoin d'un gestionnaire différent, et donc d'un HttpClient (ou, mieux encore, une fabrique qui les distribue pour vous).

Deuxièmement, si vous n'attribuez pas de gestionnaire, .NET se rabattra sur la résolution de proxy par défaut du système, y compris les variables d'environnement telles que HTTPS_PROXY. C'est pratique sur un ordinateur portable de développeur, mais surprenant dans un conteneur ; nous reviendrons donc sur la désactivation. Il en va de même si un collègue définit HttpClient.DefaultProxy quelque part dans le code de démarrage partagé : chaque client créé par la suite l'hérite, à moins que vous ne redéfinissiez le gestionnaire.

Tout au long de cet article, nous traitons tout proxy fonctionnel comme http://host:port, avec des informations d’identification facultatives. Chaque fois que vous verrez ci-dessous comment utiliser un proxy avec HttpClient en C#, c’est le modèle que nous configurons.

Configuration d’un projet C# minimal pour tester le proxy

Vérifiez votre chaîne d'outils, puis créez une application console. Nous exécutons tout sur un SDK .NET LTS récent (les exemples ont été écrits pour .NET 8 et se comportent de la même manière sur les versions ultérieures au moment de la rédaction).

dotnet --version          # expect 8.x or newer
mkdir httpclient-proxy && cd httpclient-proxy
dotnet new console

Ouvrez le dossier dans n'importe quel éditeur. Pour plus de clarté, nous allons conserver les éléments dans Program.cs pour plus de clarté. Rendez Main async afin de pouvoir await des appels HTTP sans .Result pièges :

using System.Net.Http;

static async Task Main()
{
    using var client = new HttpClient();
    var direct = await client.GetStringAsync("https://api.ipify.org");
    Console.WriteLine($"Direct IP: {direct}");
}

api.ipify.org est le point de terminaison IP-echo le moins coûteux qui soit. Exécutez dotnet run, notez l’adresse IP et conservez cette référence. Une fois que vous aurez configuré un proxy, ce même appel devrait afficher l’adresse IP de sortie du proxy à la place de la vôtre. Si ce n’est pas le cas, il s’agit d’un problème de configuration, et non d’un problème réseau.

Configuration d'un WebProxy non authentifié avec HttpClientHandler

Commençons par le cas le plus simple : un proxy gratuit ou local qui ne nécessite pas d'identifiants. La recette pour utiliser un proxy avec HttpClient en C# consiste en trois objets, dans cet ordre : WebProxy, HttpClientHandler, HttpClient.

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

var proxy = new WebProxy("http://203.0.113.10:8080")
{
    BypassProxyOnLocal = true,        // skip the proxy for localhost/loopback
    UseDefaultCredentials = false     // do not silently send Windows creds
};

var handler = new HttpClientHandler
{
    Proxy = proxy,
    UseProxy = true
};

using var client = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(15) };
var ip = await client.GetStringAsync("https://api.ipify.org");
Console.WriteLine($"Proxied IP: {ip}");

Quelques détails faciles à oublier. Le WebProxy constructeur accepte soit un Uri une chaîne de caractères, et le schéma est important : http:// pour les proxys HTTP et HTTPS qui utilisent CONNECT, et (comme nous le verrons plus tard) socks5:// pour les SOCKS. Si vous intégrez des identifiants dans l'URL, WebProxy les ignorera, ne vous donnez donc pas cette peine. BypassProxyOnLocal = true est une valeur par défaut utile ; elle vous évite de faire passer accidentellement les vérifications de santé par une adresse IP externe. UseDefaultCredentials = false empêche Windows d'envoyer automatiquement l'identité de l'utilisateur actuel à un proxy tiers, ce qui est le genre de bug que l'on ne détecte que lorsqu'un audit de sécurité analyse vos captures de paquets.

Il s'agit du modèle canonique. Tout le reste de ce guide n'est que des variantes de cette recette à trois objets. Si vous souhaitez une explication plus approfondie sur les types de proxys les plus adaptés aux tâches de scraping, le guide pratique sur les meilleurs types de proxys pour le web scraping est un bon complément de lecture.

Proxys avec authentification : NetworkCredential, erreurs 407 et PreAuthenticate

La plupart des proxys payants nécessitent une authentification. La manière .NET d'exprimer cela est NetworkCredential, associé à l' WebProxy lui-même, et non au gestionnaire :

var proxy = new WebProxy("http://gateway.example.com:8080")
{
    Credentials = new NetworkCredential("my-user", "my-pass")
};

var handler = new HttpClientHandler { Proxy = proxy, UseProxy = true };
using var client = new HttpClient(handler);

Deux pièges font trébucher presque tout le monde la première fois.

Ne placez pas les identifiants dans l'URL. new WebProxy("http://user:pass@host:8080") supprimera silencieusement la user:pass . Le segment userinfo est extrait de l' Uri mais n'est jamais utilisé comme identifiants de proxy. Transmettez toujours un NetworkCredential.

PreAuthenticate est destiné à la cible, pas au proxy. Lorsque le proxy vous rejette, il renvoie un code HTTP 407 (Authentification proxy requise). HttpClient affiche cela sous la forme d’un HttpRequestException. L'activation de HttpClientHandler.PreAuthenticate = true ne modifie pas ce comportement, car cet indicateur détermine si le serveur cible reçoit un en-tête préventif Authorization en réponse aux requêtes suivantes. Cela n'a rien à voir avec l' Proxy-Authorization , que le gestionnaire gère de lui-même une fois que vous avez défini Credentials.

Si vous continuez à obtenir un code 407 avec des identifiants qui semblent corrects, vérifiez trois choses dans l'ordre : les envoyez-vous au bon hôte (certains fournisseurs séparent le plan de contrôle de la passerelle), votre mot de passe est-il encodé en URL quelque part en amont, et votre compte est-il toujours en règle ? Notre article sur les erreurs d'état de proxy courantes et la manière de les identifier approfondit le sujet si vous avez besoin d'un guide sur l'ensemble des erreurs de proxy.

Choix d'un protocole : proxys HTTP, HTTPS et SOCKS5 en C#

HttpClient ne se soucie pas de savoir si la destination est HTTP ou HTTPS, mais le protocole proxy a son importance car il modifie la manière dont la connexion est établie.

  • Proxy HTTP (http://...) : pour les destinations HTTP, le proxy peut lire et réécrire la requête. Pour les destinations HTTPS, le client émet une CONNECT et établit un tunnel TLS de bout en bout via le proxy.
  • Proxy de terminaison HTTPS : un cas particulier où le proxy présente son propre certificat TLS à votre client et ouvre une connexion TLS distincte en amont. C'est ainsi que fonctionnent certaines API de scraping commerciales en mode proxy. Nous abordons les implications SSL dans la section dédiée ci-dessous.
  • Proxy SOCKS (socks5://, socks4://, socks4a://) : un tunnel de couche transport qui ne comprend pas le protocole HTTP. Tout ce que vous pouvez placer sur une socket TCP passe par ce tunnel.

Sur .NET moderne, SocketsHttpHandler est livré avec une prise en charge intégrée de SOCKS4, SOCKS4a et SOCKS5 (ajoutée avec .NET 6, selon le suivi des problèmes du runtime ; vérifiez dans la documentation de SocketsHttpHandler si vous utilisez une préversion non LTS). La configuration est la même WebProxy , mais le schéma est différent :

var socks = new WebProxy("socks5://socks.example.com:1080")
{
    Credentials = new NetworkCredential("u", "p")
};

var handler = new SocketsHttpHandler { Proxy = socks, UseProxy = true };
using var client = new HttpClient(handler);

Si vous devez utiliser un proxy avec HttpClient en C# pour un point de terminaison SOCKS, voici la méthode à suivre. SOCKS5 avec authentification par nom d'utilisateur/mot de passe est la norme de facto pour les fournisseurs résidentiels ; SOCKS4 est principalement obsolète.

Rotation propre des proxys avec IHttpClientFactory

La rotation des adresses IP est le point où la plupart des tutoriels échouent discrètement. L'approche naïve ressemble à ceci :

// DO NOT DO THIS in a real worker
foreach (var url in proxyUrls)
{
    var handler = new HttpClientHandler { Proxy = new WebProxy(url) };
    var client = new HttpClient(handler);   // never disposed
    var html  = await client.GetStringAsync(target);
}

Ce code provoque des fuites de sockets. Chaque HttpClient maintient son gestionnaire actif, et chaque gestionnaire conserve un pool de connexions sous-jacent. Lancez-en quelques milliers dans une boucle et vous épuiserez les ports éphémères, ce qui se traduit sous Linux par SocketException: Address already in use et sous Windows par des traces vraiment créatives WinHttpException traces.

La solution, c'est IHttpClientFactory. Il gère pour vous la durée de vie des gestionnaires, les recycle selon un calendrier et vous permet d’enregistrer un client nommé ou typé par proxy. Une petite configuration DI ressemble à ceci :

using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();

foreach (var p in proxyPool)
{
    services.AddHttpClient(p.Name, c => c.Timeout = TimeSpan.FromSeconds(20))
            .ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler
            {
                Proxy    = new WebProxy(p.Url) { Credentials = p.Creds },
                UseProxy = true,
                PooledConnectionLifetime = TimeSpan.FromMinutes(2)
            });
}

var provider = services.BuildServiceProvider();
var factory  = provider.GetRequiredService<IHttpClientFactory>();

Vous pouvez désormais choisir un proxy par requête sans rien divulguer :

var rng = new Random();
async Task<string> FetchAsync(string url)
{
    var pick   = proxyPool[rng.Next(proxyPool.Count)];
    var client = factory.CreateClient(pick.Name);
    return await client.GetStringAsync(url);
}

Le round-robin se résume à une ligne de code : conservez un Interlocked.Increment compteur et appliquez-lui un modulo par rapport à proxyPool.Count. Dans tous les cas, chaque requête est traitée par un gestionnaire valide issu du pool, et IHttpClientFactory fait tourner les gestionnaires sous-jacents sur PooledConnectionLifetime, ce qui permet d'éviter le problème de DNS obsolète de longue durée que nous aborderons dans la section suivante. Si vous souhaitez un aperçu plus complet des modèles de rotation et savoir quand les utiliser, notre analyse approfondie sur la rotation des proxys couvre en détail l'aspect algorithmique. Sachez que IHttpClientFactory les spécificités de l'API, y compris les durées de vie de l'injection de dépendances et l'intégration de Polly, peuvent varier d'une version majeure à l'autre ; vérifiez la page Microsoft Learn sur IHttpClientFactory si vous utilisez une version antérieure du runtime.

SocketsHttpHandler vs HttpClientHandler en production

Sur les versions modernes de .NET (Core 2.1+ et tous les dotnet new projet actuel), HttpClientHandler est principalement un shim de compatibilité qui délègue SocketsHttpHandler en arrière-plan. Pour la plupart des démonstrations, les deux sont interchangeables. Pour les workers et les scrapers à longue durée de vie, vous devriez SocketsHttpHandler directement car il expose les paramètres qui comptent :

var handler = new SocketsHttpHandler
{
    Proxy                     = new WebProxy("http://proxy:8080"),
    UseProxy                  = true,
    PooledConnectionLifetime  = TimeSpan.FromMinutes(2),   // recycle TCP/TLS
    PooledConnectionIdleTimeout = TimeSpan.FromSeconds(30),
    ConnectTimeout            = TimeSpan.FromSeconds(10),  // fail fast on dead proxies
    AutomaticDecompression    = System.Net.DecompressionMethods.All
};

Les deux paramètres à retenir :

  • PooledConnectionLifetime détermine la durée de vie autorisée d'une connexion dans le pool avant sa fermeture. L'implication sur le proxy / l'actualisation DNS est la véritable raison pour laquelle vous définissez ce paramètre. Un HttpClient conservera une connexion TCP unique indéfiniment par défaut, et les changements de DNS en amont (très courants pour les points de terminaison résidentiels tournants) ne seront jamais détectés. Deux minutes constituent une valeur par défaut raisonnable pour les scrapers.
  • ConnectTimeout est un plafond distinct de HttpClient.Timeout. Ce dernier couvre l'intégralité de la requête, tandis que le premier ne couvre que la poignée de main TCP avec le proxy. Le régler de manière stricte (5 à 10 secondes) est le moyen le plus économique d'empêcher les proxys morts de monopoliser les threads de travail.

AutomaticDecompression n'a aucun rapport avec les proxys mais mérite d'être mentionné ici, car la plupart des points de terminaison de scraping compressent les réponses au format gzip. La sémantique des propriétés autour de PooledConnectionLifetime et ses dérivés varient d'une version majeure à l'autre du runtime ; vérifiez donc la documentation si vous utilisez .NET 6 ou 7.

Gérer correctement la validation SSL/TLS lorsqu'un proxy se trouve dans le chemin

La présence d'un proxy dans le chemin de la requête complique la gestion du TLS, mais la règle est simple : ne désactivez jamais la validation par défaut. DangerousAcceptAnyServerCertificateValidator existe parce que Microsoft a voulu indiquer clairement que vous acceptez des certificats falsifiés si vous le configurez. Sur les proxys gratuits ou partagés, il s'agit d'une véritable vulnérabilité de type « man-in-the-middle » qui n'attend qu'à être exploitée par celui qui gère le proxy.

Il existe deux cas distincts à ne pas confondre.

Les tunnels CONNECT et SOCKS acheminent vos octets TLS de bout en bout. Le certificat que vous voyez est le véritable certificat du site de destination. La validation doit rester activée, point final. Si vous obtenez un échec de la poignée de main SSL ici, le proxy est mal configuré ou le certificat en amont est véritablement incorrect. Ne fermez pas les yeux là-dessus.

Les proxys à terminaison TLS (certaines API de scraping fonctionnent dans ce mode) effectuent intentionnellement eux-mêmes la négociation et présentent leur propre certificat. Dans ce cas, accepter une autorité de certification (CA) inconnue fait partie du contrat, mais uniquement pour ce proxy spécifique. La méthode sûre consiste à utiliser une empreinte digitale ou un callback avec CA épinglée :

var expectedThumbprint = "AABBCCDDEEFF00112233445566778899AABBCCDD";

var handler = new SocketsHttpHandler
{
    Proxy = new WebProxy("http://tls-terminating-proxy:8080"),
    SslOptions = new System.Net.Security.SslClientAuthenticationOptions
    {
        RemoteCertificateValidationCallback = (sender, cert, chain, errors) =>
        {
            if (cert is null) return false;
            return string.Equals(cert.GetCertHashString(),
                expectedThumbprint,
                StringComparison.OrdinalIgnoreCase);
        }
    }
};

Il s'agit toujours d'un assouplissement, mais limité : seul le certificat correspondant à l'empreinte digitale épinglée est accepté, et le reste du monde doit toujours passer par la validation normale de la chaîne. Si vous effectuez du scraping à grande échelle et devez utiliser un proxy avec HttpClient en C# contre une passerelle de terminaison TLS, c'est la configuration sûre pour la production.

Nouvelles tentatives, délais d'expiration et backoff exponentiel avec Polly

Les proxys tombent en panne. Les adresses IP résidentielles se déconnectent en cours de session, les plages de centres de données sont redirigées vers une adresse nulle, les cibles en amont vous limitent le débit pendant dix minutes puis reviennent. La bonne réponse consiste à réessayer avec un recul, et non à faire planter le worker.

Dans la version moderne de Polly (v8+), l'API est ResiliencePipelineBuilder. Associez un délai d'expiration court à un petit budget de tentatives afin qu'un proxy hors service échoue rapidement et qu'un proxy instable ait une seconde chance :

using Polly;
using Polly.Retry;
using Polly.Timeout;

var pipeline = new ResiliencePipelineBuilder<HttpResponseMessage>()
    .AddRetry(new RetryStrategyOptions<HttpResponseMessage>
    {
        MaxRetryAttempts = 3,
        Delay            = TimeSpan.FromMilliseconds(500),
        BackoffType      = DelayBackoffType.Exponential,
        UseJitter        = true,
        ShouldHandle     = new PredicateBuilder<HttpResponseMessage>()
            .Handle<HttpRequestException>()
            .Handle<TaskCanceledException>()
            .HandleResult(r => (int)r.StatusCode >= 500 || (int)r.StatusCode == 408)
    })
    .AddTimeout(TimeSpan.FromSeconds(15))
    .Build();

var response = await pipeline.ExecuteAsync(
    async ct => await client.GetAsync(target, ct));

Trois conseils de réglage. Gardez MaxRetryAttempts peu de tentatives (trois suffisent) ; un proxy instable mérite rarement une quatrième tentative. UseJitter = true est important lorsque vous exécutez des centaines de workers en parallèle, sinon ils réessaient tous en synchronisation et saturent le même backend. Et n'incluez pas le code 407 dans la liste des tentatives autorisées, car si les identifiants sont erronés une fois, ils le seront également lors de la tentative suivante, et vous épuiserez simplement votre budget plus rapidement. Vérifiez l'interface v8 par rapport à la documentation Polly si vous effectuez une mise à niveau depuis la v7, car plusieurs noms de classes ont changé et le style de la v7 Policy.HandleAsync ne se compile pas avec les nouveaux générateurs.

Sélection de proxy et règles de contournement par requête

Un proxy statique unique convient pour les projets amateurs. Dès que vous commencez à mélanger le trafic interne et externe, ou à acheminer différents domaines via différentes adresses IP de sortie, vous avez besoin d’une sélection par requête. WebProxy vous offre deux leviers : BypassList et une implémentation IWebProxy .

BypassList accepte les expressions régulières. Tout ce qui correspond ignore complètement le proxy, ce qui vous permet de maintenir les noms d'hôtes internes et les plages CIDR privées hors du saut externe :

var proxy = new WebProxy("http://proxy:8080")
{
    BypassProxyOnLocal = true,
    BypassList = new[] { @"^.*\.internal\.example\.com$", @"^10\.0\.0\..*$" }
};

Pour un véritable routage par hôte, implémentez IWebProxy vous-même :

sealed class HostBasedProxy : IWebProxy
{
    public ICredentials? Credentials { get; set; }
    public Uri? GetProxy(Uri destination) =>
        destination.Host.EndsWith("google.com") ? new Uri("http://us-proxy:8080")
      : destination.Host.EndsWith("yandex.ru")  ? new Uri("http://eu-proxy:8080")
      : null;
    public bool IsBypassed(Uri host) => GetProxy(host) is null;
}

Cela suffit pour gérer le routage géographique à partir d’un seul HttpClient. Le gestionnaire appelle GetProxy pour chaque requête, de sorte que la décision est dynamique et que vous n'avez pas besoin d'un client distinct par région.

Débogage des erreurs courantes du proxy HttpClient

En cas de problème, l'exception est rarement explicite. La méthode la plus rapide pour trouver une solution consiste à partir des symptômes.

Symptôme (ce que vous observez)

Cause probable

Correction en une ligne

HttpRequestException: 407 Proxy Authentication Required

Identifiants de proxy manquants ou incorrects

Définissez WebProxy.Credentials sur une NetworkCredential, ne jamais mettre user:pass@ dans l'URL

Statut 502 Bad Gateway ou 504 Gateway Timeout

Le proxy est opérationnel, l'amont est hors service ou vous limite le débit

Réessayer avec un délai d'attente ; basculer vers une autre adresse IP après le deuxième échec

HttpRequestException: The tunnel through the proxy could not be established

Proxy refusé CONNECT, souvent à cause d'un pare-feu ou d'un schéma incorrect

Vérifiez http:///socks5:// le schéma et assurez-vous que le proxy prend en charge les cibles HTTPS

AuthenticationException: échec de la négociation SSL

Proxy avec terminaison TLS sans validateur épinglé, ou certificat véritablement incorrect

Épinglez une empreinte digitale avec RemoteCertificateValidationCallback; n'activez pas DangerousAcceptAnyServerCertificateValidator à l'aveuglette

SocketException: No such host is known

Échec de la recherche DNS à l'intérieur du proxy ou pour le proxy lui-même

Vérifiez le nom d'hôte ; sur les clients de longue durée, définissez PooledConnectionLifetime pour que le DNS soit ré-évalué

La requête reste en attente jusqu'à ce que HttpClient.Timeout se déclenche

Proxy inactif, redirection infinie ou blocage CONNECT

Réglez SocketsHttpHandler.ConnectTimeout sur 5 à 10 secondes et un CancellationToken

Sporadique SocketException: Address already in use sous charge

Fuites de gestionnaires provenant de nouvellesHttpClientboucles par requête

Passer à IHttpClientFactory avec des clients nommés par proxy

En cas de doute, consignez l'URL du proxy avec l'exception. La moitié des bugs liés aux proxys disparaissent dès que vous pouvez voir quelle adresse IP a réellement échoué, au lieu de deviner parmi un pool de cinquante.

Choisir la bonne stratégie de proxy pour votre charge de travail

Il n'existe pas de réponse universelle à la question de savoir comment utiliser un proxy avec HttpClient en C# à grande échelle. La seule question est de savoir combien de ressources d'ingénierie vous souhaitez consacrer à la couche proxy par rapport à la couche de données. Choisissez la solution la plus simple qui réponde tout de même à vos exigences de fiabilité.

Stratégie

Fiabilité

Coûts de maintenance

Ciblage géographique

Quand l'utiliser

Proxys publics gratuits

Très faibles ; beaucoup sont des honeypots

Élevés ; taux de rotation constant

Aucun

Jamais pour la production. Expériences locales uniquement.

Proxys statiques authentifiés de centres de données

Acceptable pour des cibles inoffensives

Faible

Limité

API B2B, outils internes, déblocage géographique léger

Rotation DIY sur un pool résidentiel

Élevé si bien conçu

Élevé ; vous gérez les tentatives de reconnexion, les contrôles d'intégrité, les sessions persistantes et la comptabilité

Oui, si votre fournisseur expose les balises de pays

Équipes désireuses de gérer leur propre pool et disposant d'un budget pour l'ingénierie

API de scraping/proxy gérée

Élevé ; le fournisseur prend en charge les défaillances

Faible ; vous appelez un seul point de terminaison

Oui, généralement par pays

Scraping à grande échelle, cibles anti-bot, petites équipes

Une bonne règle d'or : si le code proxy de votre dépôt croît plus vite que le code d'analyse, vous payez des ingénieurs pour qu'ils soient une version moins performante d'un fournisseur géré. Montez en gamme. À l'inverse, si vous n'avez besoin que d'une poignée d'adresses IP statiques pour communiquer avec l'API d'un partenaire, ne compliquez pas les choses ; une seule WebProxy suffit.

Évoluer au-delà du « faites-le vous-même » : acheminer HttpClient via le mode proxy de WebScrapingAPI

Lorsque le calcul du « faites-le vous-même » ne tient plus la route, la solution la plus propre consiste à conserver votre HttpClient et de simplement le pointer vers un point de terminaison de proxy géré. WebScrapingAPI expose une passerelle en mode proxy qui reprend la même WebProxy + NetworkCredential recette que vous avez déjà écrite, la rotation, le ciblage géographique et la gestion anti-bot étant pris en charge côté serveur.

var proxy = new WebProxy("http://proxy.webscrapingapi.com:80")
{
    Credentials = new NetworkCredential(
        "YOUR_API_KEY",                  // username slot
        "render_js=false.country=us"     // password slot carries options
    )
};

var handler = new SocketsHttpHandler
{
    Proxy                     = proxy,
    UseProxy                  = true,
    PooledConnectionLifetime  = TimeSpan.FromMinutes(2),
    ConnectTimeout            = TimeSpan.FromSeconds(15)
};

using var client = new HttpClient(handler) { Timeout = TimeSpan.FromSeconds(60) };
var html = await client.GetStringAsync("https://example.com/product/42");

La structure est identique au modèle de proxy authentifié présenté plus haut dans l'article ; la clé API se trouve dans le champ du nom d'utilisateur et les options de requête dans celui du mot de passe. Il n’y a pas de boucle de rotation car la passerelle choisit une nouvelle adresse IP de sortie à chaque requête, pas de pipeline de réessais car les réponses ayant échoué ne sont pas facturées, et pas de logique géographique dans votre code car la sélection du pays se fait via un indicateur. Vous pouvez toujours conserver votre pipeline Polly si vous souhaitez une défense en profondeur, mais la surface d’attaque que vous gérez diminue considérablement. Considérez cela comme une option parmi d’autres dans la matrice ci-dessus, et non comme un verdict ; c’est le bon choix lorsque votre équipe est petite et que les cibles sont hostiles.

Points clés

  • Configurez le gestionnaire, pas le client. Un proxy vit sur HttpClientHandler ou SocketsHttpHandler via un WebProxy. HttpClient en soi n'a pas de Proxy propriété, c'est pourquoi vous ne pouvez pas le modifier en cours d'exécution.
  • Transmettez toujours les informations d'identification via NetworkCredential. L'intégration user:pass@ dans l'URL du proxy est ignorée sans avertissement et constitue la cause la plus fréquente d'erreurs 407 inexpliquées.
  • Utilisez IHttpClientFactory pour la rotation. Une foreach boucle qui crée un HttpClient par proxy entraînera des fuites de sockets sous charge. Clients nommés par proxy, plus PooledConnectionLifetime, corrigez cela.
  • À SocketsHttpHandler directement en production. Cela expose ConnectTimeout, PooledConnectionLifetimeet la prise en charge de SOCKS5, dont vous aurez tous besoin à terme.
  • Ne désactivez pas la validation TLS. Pour les proxys avec terminaison TLS, enregistrez une empreinte digitale. Pour les tunnels CONNECT ou SOCKS, laissez la validation activée ; les échecs dans ce cas sont de véritables bogues, pas du bruit.

FAQ : questions sur les proxys HttpClient que les développeurs posent réellement

HttpClient détecte-t-il automatiquement le proxy système ou la variable d'environnement HTTPS_PROXY, et comment puis-je désactiver cette fonctionnalité ?

Oui. En l'absence de gestionnaire attribué, HttpClient utilise le proxy par défaut du système, qui sur .NET Core 3.1+ lit également HTTP_PROXY, HTTPS_PROXY, et NO_PROXY sous Linux et macOS. Pour désactiver cette fonctionnalité, transmettez un gestionnaire explicite avec UseProxy = false, ou définissez HttpClient.DefaultProxy = new WebProxy() au démarrage.

Puis-je modifier le proxy sur une instance HttpClient existante, ou dois-je en créer une nouvelle ?

Vous avez besoin d'un nouveau client. Le proxy est lié au gestionnaire au moment de la construction, et HttpClient n'expose pas de Proxy setter. Utilisez un pool de clients préconfigurés fournis par IHttpClientFactory, ou un IWebProxy dont GetProxy(Uri) choisit dynamiquement tandis que le gestionnaire reste le même.

Pourquoi ma requête renvoie-t-elle le code 407 « Proxy Authentication Required » alors que j'ai défini des identifiants ?

Trois causes courantes : des identifiants intégrés dans l'URL (supprimés silencieusement par WebProxy), un mot de passe encodé deux fois dans l'URL quelque part en amont, ou des identifiants attribués à HttpClientHandler.Credentials au lieu de WebProxy.Credentials. Seule cette dernière option alimente le proxy. PreAuthenticate n'est d'aucune aide ici ; ce drapeau contrôle le serveur cible.

HttpClient prend-il en charge les proxys SOCKS5 sur .NET 6 et versions ultérieures ?

Oui. SocketsHttpHandler La prise en charge native de SOCKS4, SOCKS4a et SOCKS5 a été ajoutée à partir de .NET 6. Utilisez une socks5://host:port URI dans votre WebProxy et attribuez les informations d'identification via NetworkCredential si le serveur SOCKS nécessite une authentification par identifiant/mot de passe.

Quelle est la bonne façon d'annuler une requête proxy lente sans fuir de sockets ?

Transmettez un CancellationToken à partir d'un CancellationTokenSource avec un délai d'expiration raisonnable, et laisser la requête se dérouler sur OperationCanceledException. Associez le jeton à SocketsHttpHandler.ConnectTimeout afin que la négociation TCP échoue rapidement et que le socket retourne au pool au lieu de rester en suspens.

Conclusion

Voilà l'essentiel de ce que vous devez savoir pour utiliser un proxy avec HttpClient en C# sans vous mettre dans une impasse. La structure de la solution change à peine entre un exemple de cinq lignes et un worker de production : un WebProxy, un gestionnaire, un HttpClient. Ce qui change, c'est tout ce qui l'entoure. Le code de production utilise IHttpClientFactory , ce qui permet de recycler les gestionnaires, définit un ConnectTimeout pour que les proxys morts échouent rapidement, fixe les empreintes TLS au lieu de désactiver la validation, et encapsule les requêtes dans un pipeline Polly pour que les échecs passagers ne réveillent personne à 3 heures du matin.

La matrice de décision présentée plus haut dans cet article est le point le plus important à retenir. Les proxys gratuits ne sont pas gratuits une fois que l’on prend en compte le temps d’ingénierie. Les proxys statiques en centre de données sont très bien jusqu’à ce que votre cible déploie une pile anti-bot sérieuse. La rotation DIY est gratifiante à mettre en place mais coûteuse à faire fonctionner. Les API de proxy gérées échangent un budget de crédits contre le temps que vous passeriez autrement sur les contrôles de santé, les tentatives de reconnexion et la gestion des abus.

Si votre équipe en est arrivée au point où la couche proxy prend plus de temps que la couche d'analyse, le point de terminaison proxy de WebScrapingAPI s'intègre dans la même WebProxy + NetworkCredential recette que vous utilisez déjà, et vous ne payez que pour les réponses réussies. Quelle que soit la voie que vous choisissez, veillez à ce que l’abstraction reste claire : le gestionnaire en bas, les tentatives de reconnexion au milieu, votre logique métier en haut. Votre futur vous remerciera pour cette séparation.

À propos de l'auteur
Suciu Dan, cofondateur @ WebScrapingAPI
Suciu Dancofondateur

Suciu Dan est le cofondateur de WebScrapingAPI et rédige des guides pratiques destinés aux développeurs sur le web scraping avec Python et Ruby, ainsi que sur les infrastructures de proxy.

Commencez à créer

Prêt à faire évoluer votre système de collecte de données ?

Rejoignez plus de 2 000 entreprises qui utilisent WebScrapingAPI pour extraire des données Web à l'échelle de l'entreprise, sans aucun coût d'infrastructure.