Les frameworks d'automatisation de navigateur acceptent tous un proxy comme option de lancement, mais les proxies authentifiés nécessitent un traitement spécial car Chromium n'accepte pas user:pass@ dans une URL de proxy : Playwright prend les identifiants comme champs séparés (et peut définir un proxy différent par contexte de navigateur), Puppeteer nécessite page.authenticate(), et Selenium nécessite une extension ou un redirecteur local. Ce guide fournit du code fonctionnel pour chacun, puis aborde la partie qui importe plus que la syntaxe : un navigateur headless derrière une IP résidentielle reste identifiable à moins que le reste de son histoire ne corresponde.
Le fait qui façonne tout : Chromium ignore les identifiants de proxy dans l'URL
Les outils en ligne de commande et les bibliothèques Python acceptent volontiers http://user:pass@host:port. Le drapeau --proxy-server de Chromium n'accepte que host:port (avec un schéma optionnel) ; les identifiants qu'il contient sont silencieusement ignorés. Lorsque le proxy répond avec un code 407, le navigateur affiche une boîte de dialogue d'authentification — qu'aucun script d'automatisation ne surveille. Chaque framework a donc sa propre manière de fournir le nom d'utilisateur et le mot de passe, et c'est là que la plupart des problèmes de type « le proxy fonctionne avec curl mais pas dans mon script de navigateur » trouvent leur origine. Firefox est plus indulgent dans certains chemins, mais les mécanismes spécifiques aux frameworks ci-dessous fonctionnent sur tous les moteurs.
Playwright
Playwright offre un support de proxy de première classe avec des champs d'identifiants séparés, et — la fonctionnalité qui en fait le meilleur choix pour le travail multi-identité — le proxy peut être défini par contexte de navigateur, de sorte qu'un seul processus de navigateur peut héberger plusieurs sessions isolées sortant chacune d'une IP différente.
Python
from playwright.sync_api import sync_playwright
PROXY = {"server": "http://proxy.example.com:8080",
"username": "user", "password": "pass"}
with sync_playwright() as p:
# option A : un proxy pour tout le navigateur
browser = p.chromium.launch(proxy=PROXY)
page = browser.new_page()
page.goto("https://api.ipify.org?format=json")
print(page.text_content("body"))
browser.close()
# option B : un proxy différent par contexte (un navigateur, plusieurs identités)
browser = p.chromium.launch(proxy={"server": "per-context"}) # placeholder ; les vrais proxies viennent des contextes
ctx_de = browser.new_context(proxy={"server": "http://gate.provider.com:7777",
"username": "customer-country-de-session-a1", "password": "pass"},
locale="de-DE", timezone_id="Europe/Berlin")
ctx_us = browser.new_context(proxy={"server": "http://gate.provider.com:7777",
"username": "customer-country-us-session-b2", "password": "pass"},
locale="en-US", timezone_id="America/New_York")
for ctx in (ctx_de, ctx_us):
pg = ctx.new_page(); pg.goto("https://api.ipify.org?format=json"); print(pg.text_content("body"))
browser.close()
Deux détails. Les proxies par contexte dans Chromium nécessitent que le navigateur ait été lancé avec une option de proxy (n'importe quelle valeur — le placeholder documenté est {"server": "per-context"}) ; sans cela, les contextes ne peuvent pas le remplacer. Et notez locale et timezone_id définis en même temps que le proxy : une IP allemande avec un fuseau horaire américain est une contradiction que les systèmes anti-bot pondèrent, comme expliqué dans comment fonctionnent les systèmes anti-bot. Les proxies SOCKS5 sont pris en charge ("server": "socks5://host:1080") mais Chromium n'envoie pas d'identifiants aux proxies SOCKS, utilisez donc l'authentification par liste blanche d'IP avec SOCKS.
Node.js
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch({ proxy: { server: 'per-context' } });
const context = await browser.newContext({
proxy: { server: 'http://gate.provider.com:7777',
username: 'customer-country-de-session-a1', password: 'pass' },
locale: 'de-DE', timezoneId: 'Europe/Berlin',
});
const page = await context.newPage();
await page.goto('https://api.ipify.org?format=json');
console.log(await page.textContent('body'));
await browser.close();
})();
Le champ bypass de Playwright ("bypass": "localhost,*.internal") exclut des hôtes du proxy, et launch_persistent_context() accepte la même option proxy si vous avez besoin d'un répertoire de profil persistant.
Puppeteer
Puppeteer transmet le proxy comme un drapeau Chromium et authentifie par page :
const puppeteer = require('puppeteer');
(async () => {
const browser = await puppeteer.launch({
args: ['--proxy-server=http://proxy.example.com:8080'],
});
const page = await browser.newPage();
await page.authenticate({ username: 'user', password: 'pass' }); // répond au 407 pour cette page
await page.goto('https://api.ipify.org?format=json');
console.log(await page.evaluate(() => document.body.innerText));
await browser.close();
})();
page.authenticate() doit être appelé avant la navigation et sur chaque page (onglet) qui utilisera le proxy. Le proxy est à l'échelle du navigateur — Puppeteer n'a pas de paramètre de proxy par contexte comme Playwright. Pour plusieurs identités, vous avez trois options : lancer plusieurs processus de navigateur (un proxy chacun ; lourd mais simple), utiliser --proxy-server pointant vers un proxy de commutation local qui route selon un marqueur, ou passer à Playwright. Aussi utile : --proxy-bypass-list=<-loopback> si vous avez besoin que localhost soit également proxifié, et puppeteer-extra avec son plugin proxy, qui gère l'authentification et peut faire une rotation par page.
Selenium
Selenium définit le proxy via les options du navigateur. Les proxies non authentifiés (liste blanche d'IP) sont une ligne ; les proxies authentifiés sont le cas délicat.
Chrome, authentification par liste blanche
from selenium import webdriver
opts = webdriver.ChromeOptions()
opts.add_argument("--proxy-server=http://proxy.example.com:8080")
driver = webdriver.Chrome(options=opts)
driver.get("https://api.ipify.org?format=json")
print(driver.find_element("tag name", "body").text)
driver.quit()
Chrome, nom d'utilisateur / mot de passe
Le protocole WebDriver de Selenium n'a aucun hook pour répondre au 407 du proxy. Les solutions de contournement standard :
- Selenium Wire (Python) : une extension Selenium qui route le trafic via son propre proxy local et prend en charge les upstreams authentifiés :
Notez que Selenium Wire n'est plus activement maintenu ; il fonctionne toujours avec Chrome actuel pour beaucoup, mais traitez-le comme une solution temporaire.from seleniumwire import webdriver # pip install selenium-wire opts = {"proxy": {"http": "http://user:pass@proxy.example.com:8080", "https": "http://user:pass@proxy.example.com:8080"}} driver = webdriver.Chrome(seleniumwire_options=opts) - Une extension Chrome générée qui définit le proxy via
chrome.proxyet répond àonAuthRequiredavec les identifiants — un modèle bien connu ; l'extension est un manifeste plus quelques lignes de script d'arrière-plan, chargée avecopts.add_extension(...). Ne fonctionne pas en mode headless avec les anciennes versions de Chrome (les extensions n'y étaient pas prises en charge) ; fonctionne avec headless=new actuel. - Un proxy de redirection local sur
127.0.0.1qui détient les identifiants et les transmet en amont ; pointez Chrome vers lui sans authentification. Indépendant du framework et robuste. - Authentification par liste blanche du fournisseur, si votre machine a une IP statique — contourne complètement le problème.
Firefox
opts = webdriver.FirefoxOptions()
opts.set_preference("network.proxy.type", 1)
opts.set_preference("network.proxy.http", "proxy.example.com"); opts.set_preference("network.proxy.http_port", 8080)
opts.set_preference("network.proxy.ssl", "proxy.example.com"); opts.set_preference("network.proxy.ssl_port", 8080)
opts.set_preference("network.proxy.socks_remote_dns", True) # DNS distant si vous utilisez SOCKS
driver = webdriver.Firefox(options=opts)
Firefox affichera toujours une boîte de dialogue d'authentification pour les proxies avec nom d'utilisateur / mot de passe ; les approches du redirecteur local ou de la liste blanche s'appliquent.
Ce que le proxy ne résout pas dans un navigateur headless
Un vrai navigateur vous fait passer les empreintes TLS et HTTP/2 — la poignée de main est véritablement celle de Chrome (voir l'empreinte TLS). Cela ne vous fait pas, en soi, passer la couche JavaScript :
navigator.webdriveresttruesous automatisation ; les sites le vérifient en premier.- Le mode headless présente des différences de rendu et d'API (plugins manquants, chaînes de rendu WebGL spécifiques, dimensions de fenêtre) que les scripts d'empreinte testent. Le nouveau headless de Chrome (
headless=new) est beaucoup plus proche du mode avec interface mais pas identique. - WebRTC peut encore exposer la vraie IP de la machine à moins d'être désactivé ou usurpé ; un proxy n'y touche pas. Testez-le — Comment tester un proxy.
- Le fuseau horaire, la locale et la géolocalisation proviennent de la machine à moins de les définir (Playwright :
timezone_id,locale,geolocation) ; une incohérence avec le pays du proxy est un signe classique. - Le comportement : navigations instantanées, absence de mouvement de souris, timing identique à chaque exécution.
Les plugins furtifs (puppeteer-extra-plugin-stealth, playwright-stealth) corrigent les artefacts les plus évidents ; les navigateurs antidetect vont plus loin en fournissant une empreinte cohérente par profil et en exposant un port de débogage auquel votre script Playwright/Puppeteer se connecte — ce qui est la réponse de production pour le travail de compte. Voir ce qu'est un navigateur antidetect et, pour choisir le type d'IP derrière chaque profil, notre classement des proxies antidetect.
Choisir le framework pour le travail avec les proxies
| Playwright | Puppeteer | Selenium | |
|---|---|---|---|
| Proxy authentifié | Natif (champs username/password) | page.authenticate() par page | Solution de contournement nécessaire (Wire, extension, redirecteur local) |
| Proxy différent par session | Par contexte, un navigateur | Un navigateur par proxy | Un driver par proxy |
| SOCKS5 | Oui (pas d'identifiants → liste blanche) | Via drapeau (pas d'identifiants) | Via options/préférences |
| Locale / fuseau horaire par session | Options de contexte natives | APIs d'émulation / drapeaux | Préférences / CDP |
| Meilleur pour | Scraping et tests multi-identité | Automatisation Chrome centrée Node | Suites héritées, grilles multi-navigateurs |
Questions fréquemment posées
Comment utiliser un proxy authentifié dans Playwright ?
Passez proxy={"server": "http://host:port", "username": "…", "password": "…"} à launch() ou, par contexte, à new_context() (lancez d'abord avec proxy={"server": "per-context"}).
Comment utiliser un proxy avec authentification dans Puppeteer ?
Lancez avec args: ['--proxy-server=host:port'] et appelez await page.authenticate({username, password}) avant page.goto() sur chaque page.
Pourquoi Chrome ignore-t-il user:pass@ dans --proxy-server ?
Le drapeau de Chromium n'accepte que schéma://hôte:port ; les identifiants ne sont pas analysés. Il répond au 407 du proxy avec une boîte de dialogue interactive, donc l'automatisation doit fournir les identifiants via le mécanisme du framework à la place.
Puis-je utiliser un proxy différent pour chaque onglet ?
Pas par onglet. Playwright le fait par contexte de navigateur (chaque contexte est une session isolée) ; Puppeteer et Selenium nécessitent une instance de navigateur par proxy, ou un proxy de commutation local.
Un proxy rend-il Chrome headless indétectable ?
Non. Il corrige la couche IP et, étant un vrai navigateur, la couche TLS. Les drapeaux d'automatisation, les artefacts headless, WebRTC, l'incohérence de fuseau horaire et le comportement sont des signaux séparés qui nécessitent un traitement séparé.
Le proxy doit-il être résidentiel ou datacenter pour l'automatisation de navigateur ?
Quelle que soit la politique IP de la cible — le navigateur ne change pas ce calcul. Datacenter là où c'est toléré ; résidentiel ou mobile sur les sites protégés ; statique (ISP) pour les profils connectés persistants. Voir la comparaison des types de proxies.