Autenticación de Proxy Nodriver: Las Tres Soluciones que Funcionan
El resultado principal para la autenticación de proxy nodriver es una discusión en GitHub, no una guía. nodriver no tiene soporte nativo para usuario:contraseña — aquí están las tres soluciones que funcionan, y el atajo de una línea que la mayoría de la gente pasa por alto.
Busca autenticación de proxy nodriver y los diez primeros resultados son una discusión en GitHub, un repositorio de demostración, un par de hilos en Stack Overflow sobre una biblioteca diferente y un post en Reddit — ninguna guía real. La razón es simple: nodriver, el sucesor asincrónico de CDP de undetected-chromedriver (ese proyecto tiene 12.8k estrellas en GitHub y 1.3k bifurcaciones), no tiene una forma nativa de pasar usuario:contraseña a un proxy. Chrome ignora las credenciales incrustadas en un argumento de línea de comandos, y nodriver no lo soluciona. Esta guía es la página en la que ese hilo de discusión debería haberse convertido: lo que funciona, lo que no, y las tres soluciones que hacen funcionar un proxy autenticado.
El proxy simple funciona; el proxy autenticado no
Un proxy no autenticado es una línea. Pasa la dirección a través de browser_args y cada solicitud sale desde la IP del proxy:
import nodriver as uc
async def main():
browser = await uc.start(
browser_args=["--proxy-server=gate.quantumproxies.io:PORT"],
)
page = await browser.get("https://httpbin.org/ip")
print(await page.get_content()) # shows the proxy exit IP
uc.loop().run_until_complete(main())
Ahora agrega credenciales — --proxy-server=http://USUARIO:CONTRASEÑA@host:puerto — y se rompe. Chromium elimina el segmento USUARIO:CONTRASEÑA@ porque el formato del argumento no tiene un espacio para credenciales, luego el proxy responde con 407 Proxy Authentication Required y Chrome levanta un diálogo de inicio de sesión nativo que vive fuera del DOM. nodriver no puede verlo ni completarlo. Ese 407 es la misma barrera cubierta en nuestra guía para solucionar errores 407: el proxy te está rechazando, no el navegador. Así que cada solución real tiene que responder al desafío de otra manera.
Solución 1: Lista blanca de IP — sin credenciales, sin diálogo
Este es el atajo que los hilos de GitHub nunca mencionan, y es el más simple por mucho. Si tu scraper se ejecuta desde una máquina con una IP pública estable, registra esa IP en el panel de tu proveedor y elimina las credenciales por completo — la puerta de enlace te autentica por dirección de origen. El código de nodriver permanece como el fragmento simple --proxy-server anterior, sin código de autenticación en absoluto. Cada plan de QuantumProxies admite la lista blanca de IP junto con usuario:contraseña en sus proxies residenciales, por lo que este es el camino recomendado siempre que tu IP de salida sea fija. Su único límite es topológico: autentica una máquina, no un script, por lo que los runners en la nube efímeros, los contenedores detrás de NAT y las cajas CI con IPs cambiantes necesitan una de las dos siguientes soluciones.

Solución 2: responder al desafío con un manejador CDP Fetch
nodriver habla directamente el Protocolo de DevTools de Chrome, por lo que puedes interceptar el desafío de autenticación en proceso — no se necesita archivo de extensión. Habilita el dominio Fetch con handle_auth_requests=True, luego responde a cada evento AuthRequired con continue_with_auth. Dos detalles, ambos de la respuesta en la discusión #1798, son la diferencia entre funcionar y colgarse:
- Registra los manejadores antes de habilitar el dominio. La llamada interna
enablede nodriver sobrescribe tu registro de manejadores, por lo que si los agregas después nunca se disparan eventos — la razón número uno por la que la gente reporta 'no hace nada'. - Dispara y olvida las respuestas. Esperar la respuesta dentro del manejador bloquea el bucle de eventos y bloquea todo el navegador. Envuelve cada envío en
asyncio.create_taskpara que se ejecute sin bloquear.
import asyncio
import nodriver as uc
PROXY = "gate.quantumproxies.io:PORT" # host:port for --proxy-server
USER, PASS = "USER", "PASS"
class Scraper:
def __init__(self):
uc.loop().run_until_complete(self.run())
async def run(self):
browser = await uc.start(browser_args=[f"--proxy-server={PROXY}"])
self.tab = await browser.get("draft:,") # blank tab first
# 1) handlers BEFORE enabling the Fetch domain
self.tab.add_handler(uc.cdp.fetch.RequestPaused, self.on_request)
self.tab.add_handler(uc.cdp.fetch.AuthRequired, self.on_auth)
# 2) only now turn on interception with auth handling
await self.tab.send(uc.cdp.fetch.enable(handle_auth_requests=True))
page = await browser.get("https://httpbin.org/ip")
await asyncio.sleep(3)
print(await page.get_content())
async def on_auth(self, event):
# fire-and-forget: awaiting here deadlocks the loop
asyncio.create_task(self.tab.send(uc.cdp.fetch.continue_with_auth(
request_id=event.request_id,
auth_challenge_response=uc.cdp.fetch.AuthChallengeResponse(
response="ProvideCredentials", username=USER, password=PASS,
),
)))
async def on_request(self, event):
asyncio.create_task(self.tab.send(
uc.cdp.fetch.continue_request(request_id=event.request_id)))
if __name__ == "__main__":
Scraper()
Una advertencia surgió en el mismo hilo: un usuario encontró que esto funcionaba en páginas HTTP simples pero fallaba en HTTPS, y el culpable era un proxy de baja calidad, no el código — cambiar a una mejor salida lo solucionó. Esa es la lección recurrente del scraping sigiloso: el manejador responde al desafío, pero la reputación de la IP decide si el sitio te deja entrar.
Solución 3: una extensión de Chrome generada
El otro patrón comunitario construye una pequeña extensión de Chrome al inicio que establece el proxy y responde al desafío de credenciales a través de chrome.webRequest.onAuthRequired — el mismo truco que funciona en Selenium y Puppeteer. Escribes un pequeño manifiesto más un trabajador de fondo en un directorio temporal y lo cargas a través de --load-extension:
import nodriver as uc
async def main():
# ext_dir holds a Manifest V3 extension: manifest.json + worker.js that
# calls chrome.proxy.settings.set(...) and returns authCredentials from
# chrome.webRequest.onAuthRequired. Generate it once, then load it:
browser = await uc.start(browser_args=[
"--load-extension=" + ext_dir,
"--headless=new", # extensions only load in the NEW headless mode
])
page = await browser.get("https://httpbin.org/ip")
print(await page.get_content())
uc.loop().run_until_complete(main())
El manifiesto completo y el trabajador son idénticos a los archivos de Manifest V3 en nuestra guía de autenticación de proxy Selenium — cópialos literalmente, solo cambia la llamada de inicio. Dos trampas se repiten en todas partes: las extensiones se cargan solo bajo --headless=new (el simple --headless falla), y un directorio desempaquetado es más confiable a través de versiones de Chrome que un zip empaquetado. La extensión maneja cualquier tipo de proxy, lo que la convierte en la opción de respaldo cuando la ruta CDP te está dando problemas.
La cuarta opción: un relé local
Si prefieres no tocar nodriver en absoluto, ejecuta un pequeño relé local que mantenga las credenciales y presente un punto final sin autenticación en 127.0.0.1. nodriver luego apunta a la dirección de bucle invertido con el argumento simple y nunca ve un desafío. Esta es la ruta más limpia para SOCKS5, donde Chromium rechaza los proxies autenticados por completo (seguido como error de Chromium 40829748). Cubrimos el relé mínimo, las herramientas listas para usar y cuándo es excesivo en la guía de relé de proxy.
Una nota sobre SOCKS5
SOCKS5 no autenticado funciona a través del argumento — --proxy-server=socks5://host:puerto — pero SOCKS5 autenticado no, y ningún manejador CDP te salva porque Chromium nunca ha enviado soporte para nombre de usuario/contraseña SOCKS5. Las respuestas prácticas son las mismas tres: lista blanca de IP, ejecuta un relé, o usa el punto final HTTP del proveedor en su lugar. Cada plan de QuantumProxies expone tanto proxies HTTP como SOCKS5 en la misma puerta de enlace, por lo que cambiar al punto final HTTP es a menudo la solución más rápida para SOCKS5 de todas. Para los navegadores anti-detección como familia, el mapa de proxy autenticado compara nodriver, zendriver y el resto lado a lado.

Preguntas frecuentes
¿nodriver admite proxies autenticados?
No de forma nativa. Puedes pasar un proxy no autenticado a través de browser_args=["--proxy-server=host:puerto"], pero usuario:contraseña en ese argumento es eliminado por Chromium. Para autenticar, ya sea que listas blancas tu IP con el proveedor, respondas al desafío con un manejador CDP Fetch.AuthRequired, generes una extensión de Chrome para autenticación de proxy, o ejecutes un relé local que mantenga las credenciales.
¿Por qué mi manejador de autenticación nodriver no recibe eventos?
Casi siempre porque habilitaste el dominio Fetch antes de registrar los manejadores. La llamada interna enable de nodriver sobrescribe el registro, por lo que los eventos nunca llegan a tu callback. Agrega primero los manejadores RequestPaused y AuthRequired, luego llama a fetch.enable(handle_auth_requests=True). También envuelve tus respuestas en asyncio.create_task para que esperarlas no pueda bloquear el bucle.
¿Puede nodriver usar un proxy SOCKS5 con nombre de usuario y contraseña?
No. Chromium no admite SOCKS5 autenticado (error de Chromium 40829748), y nodriver hereda ese límite. SOCKS5 no autenticado funciona a través de --proxy-server=socks5://host:puerto. Para SOCKS5 autenticado, lista blanca tu IP, ejecuta un relé local que agregue las credenciales, o cambia al punto final HTTP del proveedor, que maneja la autenticación básica de manera limpia.
¿nodriver o zendriver para proxies autenticados?
Ambos comparten la misma brecha y las mismas soluciones, porque zendriver es un fork comunitario de nodriver. zendriver tiene un rastreador de problemas más activo donde la cuestión de la autenticación se discute abiertamente, pero los métodos de trabajo son idénticos. Si estás en el fork, la configuración específica del fork refleja todo aquí — el manejador CDP y la lista blanca se comportan igual.
El resumen honesto: nodriver no hará la autenticación de proxy por ti, y eso está bien una vez que conoces el mapa. Lista blanca cuando tu IP es estable, recurre al manejador CDP o a la extensión cuando no lo es, y mantén un relé en tu bolsillo trasero para SOCKS5. El código anterior responde al desafío — pero una salida residencial limpia es lo que realmente te deja pasar la puerta.