Volver al blog
Guías
Mihnea-Octavian ManolacheLast updated on May 1, 202613 min read

Cómo utilizar un proxy en Node-Fetch: Guía práctica

Cómo utilizar un proxy en Node-Fetch: Guía práctica
En resumen: Node-Fetch no tiene un controlador de proxy integrado, por lo que hay que configurar un agente HTTP, HTTPS o SOCKS5 en la solicitud mediante su agent opción. Esta guía explica paso a paso cómo utilizar un proxy en Node-Fetch de principio a fin: proxies HTTP y HTTPS autenticados, SOCKS5, rotación, reintentos, casos extremos de TLS, resolución de problemas y la ruta undici moderna para la recuperación nativa de Node 18+.

Si alguna vez te has quedado mirando un 403 de un destino del que solías extraer datos sin problemas, ya sabes por qué existe este artículo. Aprender a usar un proxy en Node-Fetch marca la diferencia entre un script que funciona en tu portátil y uno que sobrevive en CI con una IP diferente, en un país diferente, frente a una pila anti-bot real. La buena noticia: cómo usar un proxy en Node-Fetch se reduce a una pequeña interfaz API, y el resto es pegamento operativo.

Node-Fetch es un popular cliente HTTP para Node.js que lleva el window.fetch al servidor. Es pequeño, asíncrono y agradable de usar, pero intencionadamente no incluye una proxy opción. En su lugar, expone un agent ranura, y tú conectas un agente proxy externo a ella. Esa única elección de diseño es el mecanismo detrás de todas las recetas que siguen.

Esta guía es independiente del proveedor y se centra en el código. Configurarás un proxy HTTP/HTTPS, enviarás tu primera solicitud a través del proxy, añadirás credenciales de forma segura, cambiarás a SOCKS5, rotarás a través de un grupo de servidores, añadirás tiempos de espera y reintentos, y verificarás que el tráfico realmente sale a través del proxy. También cubriremos la alternativa para Node 18+ utilizando ProxyAgent, además de una matriz de resolución de problemas para los errores con los que te encontrarás el primer día.

Cómo usar un proxy en Node-Fetch: por qué necesitas un agente

Node-Fetch no tiene un parámetro de proxy integrado. Para enrutar las solicitudes a través de un proxy, debes pasar un http.Agent (o https.Agent) a la agent opción en cada fetch() llamada. Los paquetes de la comunidad https-proxy-agent y socks-proxy-agent implementan esa interfaz y canalizan tu tráfico a través del proxy que les indiques.

Conclusión: lo único que necesitas saber sobre Node-Fetch es que la agent opción existe y acepta cualquier agente compatible. Todo lo demás —HTTP, HTTPS, SOCKS5, autenticación, peculiaridades de TLS, rotación— reside en la capa del agente. Esto mantiene tu código de fetch idéntico en todos los proveedores y te permite cambiar el transporte sin reescribir la lógica de negocio.

Configuración del proyecto y dependencias

Necesitas una versión reciente de Node.js LTS. El node-fetch README indica una versión mínima exacta que ha variado entre versiones, así que compárala con el node-fetch antes de fijar tu matriz de CI. Cualquier versión de la línea LTS actual suele ser segura.

Elige una node-fetch versión principal deliberadamente. La versión 3 es solo ESM, lo que significa que debes establecer "type": "module" en tu package.json (o utiliza .mjs archivos) y la carga con import. La versión 2 sigue funcionando en proyectos CommonJS y es intercambiable a efectos de proxy. Las dos versiones tienen un comportamiento prácticamente idéntico, por lo que la v2 es una opción perfectamente razonable si tu código aún no está en ESM.

Instala el agente proxy junto con Node-Fetch:

# CommonJS friendly
npm install node-fetch@2 https-proxy-agent

# ESM (requires "type": "module" in package.json)
npm install node-fetch https-proxy-agent

Enviar la primera solicitud a través de un proxy HTTP

Una vez instalados los paquetes, el proceso es mecánico: crea una URL de proxy, pásala a HttpsProxyAgenty pasa ese agente a fetch(). La misma clase de agente funciona tanto si tu destino es http:// o https://, ya que tuneliza HTTPS a través del proxy con CONNECT.

// proxy-fetch.js (CommonJS, node-fetch v2)
const fetch = require('node-fetch');
const { HttpsProxyAgent } = require('https-proxy-agent');

async function main() {
  const proxyUrl = `http://${process.env.PROXY_HOST}:${process.env.PROXY_PORT}`;
  const agent = new HttpsProxyAgent(proxyUrl);

  const res = await fetch('https://ifconfig.me/all.json', { agent });
  const body = await res.json();
  console.log('Outbound IP:', body.ip_addr);
}

main().catch(console.error);

Algunos detalles que importan discretamente al implementar esto:

  • Utiliza una importación desestructurada ({ HttpsProxyAgent }) para https-proxy-agent v6 y versiones posteriores. La forma de exportación predeterminada ha cambiado entre versiones principales, y una importación incorrecta es una causa habitual de undefined is not a constructor.
  • Reutiliza el agente en todas las solicitudes dirigidas al mismo proxy. Crear un agente nuevo por cada llamada funciona, pero se pierde el agrupamiento de conexiones y se paga un handshake TLS cada vez.
  • Accede primero a un punto final de eco de IP conocido (ifconfig.me, ident.me, o ipinfo.io/json). Si no ves que te devuelve la IP del proxy, no sigas adelante; nada más funcionará hasta que ese caso básico lo haga.

Autenticación en un servidor proxy

La mayoría de los proxies de pago requieren credenciales. La convención es incluirlas en la URL con el http://USERNAME:PASSWORD@HOST:PORT forma, que https-proxy-agent se analiza automáticamente:

const user = encodeURIComponent(process.env.PROXY_USER);
const pass = encodeURIComponent(process.env.PROXY_PASS);
const proxyUrl = `http://${user}:${pass}@${process.env.PROXY_HOST}:${process.env.PROXY_PORT}`;
const agent = new HttpsProxyAgent(proxyUrl);

Hay dos cosas que suelen dar problemas aquí. En primer lugar, codificar las credenciales en los archivos fuente las filtra a través del historial de Git; guárdalas en variables de entorno (o en un gestor de secretos) e inyéctalas en tiempo de ejecución. En segundo lugar, los caracteres especiales en las contraseñas (@, :, /, #) corrompen el análisis de la URL de forma silenciosa, y obtendrás un 407 Proxy Authentication Required en lugar de un error de análisis. Envolver el nombre de usuario y la contraseña en encodeURIComponent() elimina todo ese tipo de errores.

Uso de proxies SOCKS5 con Node-Fetch

Cuando tu proveedor te proporcione un punto final SOCKS5, cambia el agente. A Node-Fetch no le importa el protocolo; simplemente llama al agente que le indiques. Instala socks-proxy-agent:

npm install socks-proxy-agent
const fetch = require('node-fetch');
const { SocksProxyAgent } = require('socks-proxy-agent');

// socks5h:// resolves DNS through the proxy; socks5:// resolves locally.
const proxyUrl = `socks5h://${process.env.PROXY_USER}:${process.env.PROXY_PASS}` +
                 `@${process.env.PROXY_HOST}:${process.env.PROXY_PORT}`;
const agent = new SocksProxyAgent(proxyUrl);

fetch('https://ifconfig.me/all.json', { agent })
  .then(r => r.json())
  .then(console.log);

Prefiere el socks5h:// esquema cuando realices scraping, ya que reenvía la resolución de DNS a través del proxy. El socks5:// resuelve el nombre de host en tu máquina, lo que filtra tu DNS real y, en parte, anula el propósito de enrutar a través del proxy en primer lugar.

Trabajar con destinos HTTPS y certificados autofirmados

Algunos productos de proxy, especialmente los servicios de «desbloqueo web» de tipo interceptación, presentan su propio certificado TLS a tu cliente y vuelven a firmar la respuesta del servidor. Node rechazará ese protocolo de enlace de forma predeterminada con UNABLE_TO_VERIFY_LEAF_SIGNATURE o SELF_SIGNED_CERT_IN_CHAIN.

La solución rápida es establecer process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'. No lo hagas en producción. Desactiva la verificación de certificados para todas las solicitudes salientes del proceso, incluidas aquellas que sí deseas que se verifiquen.

Limítalo en su lugar al propio agente:

const agent = new HttpsProxyAgent(proxyUrl, { rejectUnauthorized: false });

Esto mantiene intacta la postura TLS del resto de tu aplicación. El nombre exacto de la opción del constructor y cómo se propaga ha cambiado a lo largo de https-proxy-agent versiones principales, así que vuelve a consultar la página de npm del paquete cuando actualices, y evita esta bandera por completo si puedes fijar un paquete de CA en su lugar.

Proxies rotativos para un scraping resiliente

Muchos sitios utilizan limitación de velocidad basada en IP y detección de bots, por lo que una sola IP de proxy se verá limitada o bloqueada en cuanto escales. La rotación a través de un grupo distribuye la carga y hace que cada solicitud parezca provenir de un usuario diferente. Hay dos patrones que conviene conocer sobre cómo utilizar un proxy en Node-Fetch a gran escala: una selección aleatoria por solicitud y un recorrido determinista por turnos. Ambos se basan en la misma idea de «un agente por proxy» que ya conoces.

Selección de un proxy aleatorio en cada solicitud

Cuando lo único que te importa es la dispersión, indexa aleatoriamente en el grupo e instancia un nuevo agente por llamada:

const proxies = process.env.PROXY_POOL.split(','); // host:port,host:port,...

function randomAgent() {
  const pick = proxies[Math.floor(Math.random() * proxies.length)];
  return new HttpsProxyAgent(`http://${pick}`);
}

await fetch(targetUrl, { agent: randomAgent() });

Si el mismo proxy atiende varias solicitudes consecutivas, almacena en caché su agente en un Map indexado por la URL del proxy para mantener la reutilización de la conexión.

Iteración secuencial a través de una lista de proxies

Para una depuración reproducible o un comportamiento simple de rotación circular, recorre la lista con un contador. La rotación secuencial también facilita la atribución de métricas de éxito por proxy, lo cual es importante una vez que empiezas a retirar proxies inactivos:

let i = 0;
for (const url of urls) {
  const agent = new HttpsProxyAgent(`http://${proxies[i % proxies.length]}`);
  await fetch(url, { agent });
  i++;
}

Añadir reintentos, tiempos de espera y gestión de errores

El tráfico de producción a través de proxies públicos falla de formas curiosas: bloqueos, sockets a medio abrir, ECONNRESET, tormentas repentinas de 5xx. Una configuración robusta combina un tiempo de espera por solicitud, un bucle de reintentos limitado con retroceso y un disyuntor que retira un proxy tras fallos repetidos. (Más información sobre la estrategia de rotación en nuestra guía detallada sobre la rotación de proxies para el web scraping).

async function fetchWithProxy(url, getAgent, opts = {}) {
  const { tries = 3, timeoutMs = 10_000 } = opts;
  let lastErr;

  for (let attempt = 1; attempt <= tries; attempt++) {
    const ctrl = new AbortController();
    const t = setTimeout(() => ctrl.abort(), timeoutMs);
    try {
      const res = await fetch(url, { agent: getAgent(), signal: ctrl.signal });
      if (res.ok) return res;
      if (res.status === 407 || res.status === 403) throw new Error(`status ${res.status}`);
    } catch (err) {
      lastErr = err;
      const backoff = 2 ** attempt * 250 + Math.random() * 250;
      await new Promise(r => setTimeout(r, backoff));
    } finally {
      clearTimeout(t);
    }
  }
  throw lastErr ?? new Error('exhausted retries');
}

Lleva un recuento de fallos por URL de proxy en un pequeño objeto y, cuando uno supere un umbral (tres fallos en un minuto, por ejemplo), elimínalo del grupo hasta que pase un periodo de enfriamiento. Esa única regla es la parte que la mayoría de los tutoriales omiten cuando explican cómo usar un proxy en Node-Fetch, y es lo que evita que un puñado de IP inactivas contamine cada reintento. Combínalo con AbortController para que un proxy bloqueado nunca atascara tu worker para siempre.

Verificar que el proxy se está utilizando realmente

Nunca confíes en un proxy que no hayas verificado. La prueba más sencilla es un diff: envía una solicitud a un punto final de eco de IP/geo dos veces, una con el agente y otra sin él, y compara. La IP y el país deben cambiar.

const direct = await fetch('https://ipinfo.io/json').then(r => r.json());
const proxied = await fetch('https://ipinfo.io/json', { agent }).then(r => r.json());
console.log('direct  :', direct.ip, direct.country);
console.log('proxied :', proxied.ip, proxied.country);

Si las dos líneas coinciden, tu agente está siendo ignorado, normalmente debido a un error en la forma de importación o a una clave de opción mal escrita. Añade esta comprobación a la prueba de inicio de tu scraper para que una implementación silenciosamente directa falle de forma evidente.

Recuperación nativa en Node 18+ frente a Node-Fetch

Node 18 incluye un fetch impulsado por undici, y muchos equipos han abandonado el node-fetch por completo. El problema: el integrado fetch no acepta la agent , por lo que https-proxy-agent no se conectará directamente. El equivalente nativo es ProxyAgent, establecido como el distribuidor global:

import { ProxyAgent, setGlobalDispatcher } from 'undici';

setGlobalDispatcher(new ProxyAgent(process.env.PROXY_URL));

const res = await fetch('https://ifconfig.me/all.json');
console.log(await res.json());

La API del proxy de undici ha cambiado a lo largo de las versiones (autenticación en la URL, encabezados personalizados, distribuidores con ámbito de solicitud), así que consulta la documentación actual de undici antes de fijar nada. El modelo mental sigue correspondiendo claramente a cómo usar un proxy en Node-Fetch, solo que en la capa del distribuidor en lugar de por solicitud.

Solución de problemas comunes de proxy de Node-Fetch

La mayoría de node-fetch errores de proxy se pueden resumir en una pequeña matriz:

Síntoma

Causa probable

Solución

ECONNREFUSED

Host o puerto de proxy incorrectos, o el proxy está inactivo.

Telnet/nc host:puerto; cambia a otro proxy.

ETIMEDOUT / solicitud bloqueada

No AbortController tiempo de espera, o el proxy está descartando paquetes de forma silenciosa.

Envuelve cada recuperación con un tiempo de espera por solicitud y vuelve a intentarlo en un proxy diferente.

407 Proxy Authentication Required

Faltan las credenciales o la URL está dañada.

Codifica el usuario y la contraseña en URL; comprueba que las variables de entorno estén cargadas.

SELF_SIGNED_CERT_IN_CHAIN

El proxy presenta su propio certificado TLS.

Configure rejectUnauthorized: false en el agente (con ámbito), no en la variable de entorno global.

Cannot find module 'node-fetch'

ESM v3 importado desde un archivo CommonJS.

Añadir "type": "module" o cambiar a una versión anterior node-fetch@2.

HttpsProxyAgent is not a constructor

Formato de importación incorrecto para v6+.

Uso const { HttpsProxyAgent } = require('https-proxy-agent').

Cuando un objetivo sigue fallando en varios proxies que funcionan correctamente, el cuello de botella suele ser la identificación de bots, más que la capa de proxy. En ese punto, por mucho que se refine el uso del proxy en Node-Fetch, no servirá de nada; se necesita una capa de solicitud que también gestione la identificación de bots.

Conclusiones clave y próximos pasos

El mecanismo que subyace al uso de un proxy en Node-Fetch es una opción: agent. Elige la clase de agente adecuada (HttpsProxyAgent para HTTP/HTTPS, SocksProxyAgent para SOCKS5), introdúcelle una URL correctamente codificada, y el resto es rotación, reintentos y verificación. A partir de aquí, los siguientes pasos lógicos son implementar la lógica de rotación por capas y comparar clientes HTTP alternativos para Node.js.

Conclusiones clave

  • Node-Fetch no tiene proxy opción; se integra el soporte pasando un Agent (de https-proxy-agent o socks-proxy-agent) al agent campo en fetch().
  • Elige la especialidad deliberadamente: node-fetch@3 es solo para ESM y necesita "type": "module", mientras que node-fetch@2 es la opción compatible con CommonJS con el mismo comportamiento de proxy.
  • Codifica siempre las credenciales en URL y léelas desde variables de entorno. Un 407 Proxy Authentication Required suele ser más un error de análisis que un fallo real de autenticación.
  • El código de proxy de Node-Fetch para producción combina AbortController tiempos de espera, retroceso exponencial y el abandono de un proxy inactivo tras varios fallos, no solo un try/catch alrededor de fetch.
  • En Node 18+ con fetch, la agent opción no funciona; usa la de undici ProxyAgent plus setGlobalDispatcher y vuelve a comprobar la superficie de la API con la documentación actual de undici.

Preguntas frecuentes

¿Node-Fetch v3 admite proxies de forma nativa?

No. Ni la v2 ni la v3 de Node-Fetch implementan una proxy opción. El mecanismo de proxy es idéntico en ambas versiones principales: instala https-proxy-agent (o socks-proxy-agent), crea un agente a partir de tu URL de proxy y pásalo a fetch() a través del agent . La única diferencia entre la v2 y la v3 es el sistema de módulos: ESM en la v3 frente a CommonJS en la v2.

¿Puedo reutilizar el mismo HttpsProxyAgent en muchas llamadas fetch?

Sí, y deberías hacerlo. Reutilizar un agente por cada par (proxyUrl, host de destino) permite que el grupo de sockets subyacente mantenga las conexiones activas, lo que elimina un protocolo de enlace TLS de cada solicitud y reduce notablemente la latencia bajo carga. Solo crea un nuevo agente cuando cambie la propia URL del proxy, por ejemplo, durante la rotación. Almacénalos en caché en un Map clave basada en la URL del proxy para mantener tanto la reutilización como la rotación.

¿Cuál es la diferencia entre HttpProxyAgent y HttpsProxyAgent, y cuándo necesito cada uno?

HttpProxyAgent túneles http:// a través de un proxy sin CONNECT. HttpsProxyAgent enviar una solicitud HTTP CONNECT al proxy y luego ejecuta TLS hacia el destino, que es lo que necesitas para cualquier https:// URL. En la práctica, HttpsProxyAgent cubre ambos protocolos de forma segura y es la recomendación predeterminada. Recurre a HttpProxyAgent solo cuando tengas una razón específica y te dirijas a puntos finales que solo admiten HTTP.

¿Por qué mi solicitud Node-Fetch a través del proxy devuelve un 407 Proxy Authentication Required?

Un 407 significa que el proxy no ha aceptado tus credenciales, pero la causa suele estar más arriba del propio proxy. La más común es un carácter especial en la contraseña que interrumpe el análisis de la URL. Envuelve ambos campos en encodeURIComponent y recárgalos desde variables de entorno. Después, comprueba bien el formato de credenciales que espera tu proveedor (algunos requieren un token de sesión, un código de país o un prefijo de sesión en el nombre de usuario).

¿Cómo utilizo un proxy con la función fetch integrada en Node.js 18+ en lugar de node-fetch?

La función nativa fetch en Node 18+ utiliza undici, que ignora la agent opción. Instala undici, crea una ProxyAgent desde tu URL de proxy y regístralo con setGlobalDispatcher. A partir de ahí, todas las fetch() se enruta a través del proxy sin necesidad de más cambios en el código. La API de undici ha evolucionado a lo largo de las versiones, así que comprueba los nombres actuales de las opciones en la documentación de undici antes de fijarlas.

Conclusión

Una vez que interiorices la agent opción, todas las recetas de esta guía son pequeñas variaciones de la misma idea. HTTP, HTTPS y SOCKS5 comparten una misma firma de recuperación; la autenticación es una codificación de URL realizada correctamente; la rotación, los reintentos y el omisión de proxies inactivos se encuentran en una fina envoltura alrededor de fetch(); y la ruta de undici para Node 18+ es el mismo modelo mental traducido a un distribuidor global.

No subestimes la capa operativa. Los proxies gratuitos son inútiles en producción: la baja reputación de IP, el alto tiempo de inactividad y el comportamiento impredecible de TLS acabarán con tu presupuesto de errores. Un conjunto limpio de direcciones residenciales o de centros de datos, junto con el envoltorio de tiempo de espera y reintentos mencionado anteriormente, es lo que convierte un fragmento de código en un scraper implementable.

Si prefieres no gestionar tú mismo la infraestructura del proxy, la API Scraper de WebScrapingAPI se encarga de la rotación de proxies, la mitigación contra bots y los reintentos detrás de un único punto final, para que puedas mantener tu node-fetch código y cambiar la capa de solicitudes. A partir de aquí, los siguientes pasos lógicos son estructurar la estrategia de rotación y elegir el cliente HTTP de Node.js adecuado para tu carga de trabajo, temas que nuestras guías complementarias tratan en profundidad.

Acerca del autor
Mihnea-Octavian Manolache, Desarrollador Full Stack @ WebScrapingAPI
Mihnea-Octavian ManolacheDesarrollador Full Stack

Mihnea-Octavian Manolache es ingeniero Full Stack y DevOps en WebScrapingAPI, donde se encarga de desarrollar funciones para los productos y de mantener la infraestructura que garantiza el buen funcionamiento de la plataforma.

Empieza a crear

¿Estás listo para ampliar tu recopilación de datos?

Únete a más de 2000 empresas que utilizan WebScrapingAPI para extraer datos de la web a escala empresarial sin ningún gasto de infraestructura.