Esta es la trampa en la que cae exactamente una vez todo desarrollador de automatización de navegadores: añades --proxy-server=http://user:pass@host:port a las flags de lanzamiento de Chrome, el navegador arranca bien, y luego cada página se queda colgada en un popup de autenticación que tu script no puede ver ni pulsar. Chrome elimina silenciosamente las credenciales de la flag del proxy. Sin error, sin línea de log, solo un diálogo modal en un navegador que nadie está mirando.
Esta guía es el mapa completo de los métodos de autenticación de proxy que de verdad funcionan en Selenium y Playwright en 2026 — cuál usar, cuál evitar, y el código exacto para cada uno. (Para la selección de proxies y la estrategia de rotación en estas herramientas, consulta Usar proxies con Playwright & Puppeteer — este artículo trata puramente sobre hacer que las credenciales pasen.)
Los clientes HTTP de línea de comandos parsean las credenciales de las URL de proxy y responden al 407 Proxy Authentication Required del proxy con una cabecera Proxy-Authorization automáticamente. Chromium no: --proxy-server solo acepta scheme://host:port, y cuando vuelve el 407 muestra un diálogo interactivo. En modo headless ni siquiera hay diálogo — la petición simplemente falla.
Así que la pregunta nunca es "cómo pongo la contraseña en la URL" — es "qué capa responde al 407 por mí". Cuatro respuestas que funcionan, la mejor primero.
Playwright resolvió el problema correctamente: responde por sí mismo a los desafíos de autenticación de proxy mediante CDP. Las credenciales van en las opciones de lanzamiento o de contexto:
# Python
from playwright.sync_api import sync_playwright
with sync_playwright() as p:
browser = p.chromium.launch(
proxy={
"server": "http://us.jibaoproxy.com:913",
"username": "USERNAME-session-job1",
"password": "PASSWORD",
}
)
// Node.js - and per-context, which is where it gets powerful:
const browser = await chromium.launch({
proxy: { server: 'per-context' } // declare intent at launch
});
const ctx = await browser.newContext({
proxy: {
server: 'http://us.jibaoproxy.com:913',
username: 'USERNAME-session-job2',
password: 'PASSWORD',
}
});
Los proxies por contexto significan que un solo proceso de navegador puede ejecutar N contextos sobre N sesiones sticky distintas — el cimiento de toda configuración seria de scraping con Playwright. Si estás en Playwright, ya terminaste; salta a la sección de verificación. Todo lo de abajo existe porque Selenium no tiene equivalente.
selenium-wire envuelve Selenium con un proxy MITM local que gestiona la autenticación ascendente:
from seleniumwire import webdriver # pip install selenium-wire
options = {
"proxy": {
"http": "http://USERNAME:[email protected]:913",
"https": "https://USERNAME:[email protected]:913",
}
}
driver = webdriver.Chrome(seleniumwire_options=options)
driver.get("https://example.com")
Advertencias que deberías conocer antes de producción:
El enfoque clásico: una pequeña extensión generada que configura el proxy y responde a la autenticación mediante chrome.webRequest.onAuthRequired. Sin MITM, sin proceso extra, funciona en Selenium puro:
import zipfile, json
HOST, PORT = "us.jibaoproxy.com", 913
USER, PASS = "USERNAME-session-sel1", "PASSWORD"
manifest = {
"version": "1.0.0", "manifest_version": 2, "name": "Proxy Auth",
"permissions": ["proxy", "webRequest", "webRequestBlocking", ""],
"background": {"scripts": ["background.js"]},
}
background = f"""
chrome.proxy.settings.set({{value: {{mode: "fixed_servers", rules: {{
singleProxy: {{scheme: "http", host: "{HOST}", port: {PORT}}}
}}}}, scope: "regular"}}, () => {{}});
chrome.webRequest.onAuthRequired.addListener(
() => ({{authCredentials: {{username: "{USER}", password: "{PASS}"}}}}),
{{urls: [""]}}, ["blocking"]
);
"""
with zipfile.ZipFile("proxy_auth.zip", "w") as zp:
zp.writestr("manifest.json", json.dumps(manifest))
zp.writestr("background.js", background)
from selenium import webdriver
opts = webdriver.ChromeOptions()
opts.add_extension("proxy_auth.zip")
driver = webdriver.Chrome(options=opts)
Dos trampas: las extensiones no se cargan en el modo headless clásico — usa --headless=new (Chrome 109+) o ejecuta en modo headed; y los scripts de fondo de Manifest V2 todavía funcionan para extensiones de automatización cargadas de forma lateral, pero vigila el calendario de obsolescencia de MV2 de Chrome — el equivalente en MV3 necesita un service worker y el mismo listener onAuthRequired.
Ejecuta un pequeño proxy local que guarde las credenciales; apunta el navegador a localhost sin autenticación alguna. Funciona con todos los navegadores, todos los drivers, todos los lenguajes, headless o no:
# Using pproxy (pip install pproxy) - terminal 1:
pproxy -l http://127.0.0.1:8899 \
-r "http://USERNAME:[email protected]:913"
# Your script - terminal 2: plain Selenium, no auth needed
opts = webdriver.ChromeOptions()
opts.add_argument("--proxy-server=http://127.0.0.1:8899")
driver = webdriver.Chrome(options=opts)
Esta es también la respuesta más limpia para Firefox/geckodriver (donde el truco de la extensión no aplica) y para entornos exóticos como las pruebas de apps Electron. Coste: una pieza móvil más que supervisar, y el enrutamiento por contexto requiere un puerto de reenviador por sesión.
| Método | Funciona en | Headless | Sesiones por contexto | Veredicto de producción |
|---|---|---|---|---|
| Nativo de Playwright | Playwright | Sí | Sí | Opción por defecto |
| selenium-wire | Selenium (Py) | Sí | Por driver | Bien a escala moderada |
| Extensión de autenticación | Selenium (cualquier lenguaje) | Solo --headless=new | Por driver | Sólido, vigila MV3 |
| Reenviador local | Todo | Sí | Un puerto por sesión | Respaldo universal |
El retroceso silencioso a tu IP real es el modo de fallo que hace que banean cuentas. Asegura la IP de salida al inicio de cada ejecución:
ip = driver.execute_script(
"return fetch('https://api.ipify.org').then(r => r.text())"
) if hasattr(driver, 'execute_script') else page.evaluate(
"fetch('https://api.ipify.org').then(r => r.text())"
)
assert ip != MY_REAL_IP, "Proxy not applied - aborting before we leak"
--proxy-server; Chrome las descartó. Usa uno de los cuatro métodos.ERR_PROXY_CONNECTION_FAILED — host/puerto incorrectos o protocolo no coincidente. Prueba primero la misma línea con curl -x; si curl funciona, el problema es el cableado de tu navegador.--headless=new.Los navegadores no leen las URL de proxy user:pass@ — algo tiene que responder al 407 por ellos. En Playwright ese algo está integrado; en Selenium es selenium-wire, una extensión de autenticación generada o un reenviador local. Elige según tu stack a partir de la tabla comparativa, y luego asegura la IP de salida en cada ejecución para que un fallo silencioso de autenticación nunca pueda filtrar tu dirección real a mitad del crawl.
500MB de tráfico gratis — una puerta de enlace, sticky o rotativa, HTTP y SOCKS5.
Comenzar prueba gratuitaLos nuevos usuarios reciben 500MB al registrarse, más un bono en la primera recarga. Oferta por tiempo limitado.