Configuración de proxy en Node.js, conexión de proxies HTTP y SOCKS5 con fetch y https
Esta guía explica cómo conectar el proxy HTTP de Socks5Proxy con ProxyAgent de undici y el proxy SOCKS5 con socks-proxy-agent en Node.js 18 o superior, y cómo rotar la lista usando la API.
¿Qué se necesita para usar un proxy en Node.js?
El fetch integrado en Node.js funciona con la biblioteca undici y, por defecto, no lee la configuración del proxy. Por eso, para el proxy HTTP se pasa ProxyAgent de undici como dispatcher, y para el proxy SOCKS5 se pasa socks-proxy-agent como agente al módulo https de Node.
El ejemplo usa IP 198.51.100.23, puerto HTTP 8000, puerto SOCKS5 1080, usuario spx_user, contraseña spx_pass. Todo el código está en formato ES módulo (.mjs).
npm install undici socks-proxy-agent¿Cómo conectar un proxy HTTP a fetch?
Coloca la dirección del proxy en ProxyAgent y el nombre de usuario y la contraseña en la opción token como valor de autenticación Basic. Pásalo como dispatcher al fetch importado de undici, y esa solicitud pasará por el proxy. Las direcciones HTTPS se transmiten cifradas mediante el método CONNECT.
Reutilizar ProxyAgent en múltiples requests acelera conexiones. Crea uno por proxy y rota entre ellos.
import { fetch, ProxyAgent } from "undici";
const agent = new ProxyAgent({
uri: "http://198.51.100.23:8000",
token: "Basic " + Buffer.from("spx_user:spx_pass").toString("base64"),
});
const res = await fetch("https://api.ipify.org?format=json", { dispatcher: agent });
console.log(res.status, await res.json()); // "ip" should be the proxy IP¿Cómo conectar un proxy SOCKS5?
socks-proxy-agent es un agente que se usa con los módulos http y https de Node. Usa socks5h para que el proxy resuelva los nombres de dominio. El siguiente ejemplo pasa el agente a https.get y recopila el cuerpo de la respuesta para imprimirlo.
Las bibliotecas que funcionan sobre el módulo http, como axios, también pueden usar SOCKS5 pasando el mismo agente en httpAgent y httpsAgent.
import https from "node:https";
import { SocksProxyAgent } from "socks-proxy-agent";
const agent = new SocksProxyAgent("socks5h://spx_user:spx_pass@198.51.100.23:1080");
https.get("https://api.ipify.org?format=json", { agent }, (res) => {
let body = "";
res.on("data", (chunk) => (body += chunk));
res.on("end", () => console.log(res.statusCode, body));
}).on("error", (err) => console.error("request failed:", err.message));¿Cómo obtener y rotar una lista por API?
Al iniciar, obtienes la lista por API y creas un agente por cada proxy, usando el siguiente agente para cada solicitud. El siguiente código obtiene la lista HTTP en formato ip:port:user:pass, crea un array de ProxyAgent, y si falla, reintenta con el siguiente proxy.
Usa AbortSignal.timeout para limitar el tiempo por solicitud. Las solicitudes sin respuesta ralentizan todo el proceso.
import { fetch, ProxyAgent } from "undici";
const API = "https://socks5proxy.io/api/v1";
const KEY = "YOUR_API_KEY";
const text = await (await fetch(API + "/proxies/download?format=ip:port:user:pass&protocol=http", {
headers: { Authorization: "Bearer " + KEY },
})).text();
const agents = text.trim().split("\n").map((line) => {
const [ip, port, user, pass] = line.trim().split(":");
return new ProxyAgent({
uri: "http://" + ip + ":" + port,
token: "Basic " + Buffer.from(user + ":" + pass).toString("base64"),
});
});
let i = 0;
async function get(url, tries = 3) {
for (let t = 0; t < tries; t++) {
const dispatcher = agents[i++ % agents.length];
try {
const res = await fetch(url, { dispatcher, signal: AbortSignal.timeout(20000) });
if (res.ok) return await res.text();
} catch {
// try the next proxy
}
}
return null;
}
console.log((await get("https://example.com/"))?.length);¿Cómo cambia el código al usar autorización por IP?
Si registras la IPv4 del servidor donde se ejecuta Node.js en el panel de control, puedes usar ProxyAgent sin la opción de token, solo con la dirección. También puedes usar SOCKS5 sin nombre de usuario y contraseña, como socks5h://198.51.100.23:1080. Esto facilita la implementación en varios servidores sin almacenar contraseñas en variables de entorno o código.
Si estás usando axios, ¿cómo te conectas?
axios funciona sobre los módulos http y https de Node, por lo que puedes pasar un agente para usar proxies. Para SOCKS5, usa socks-proxy-agent en httpAgent y httpsAgent, y establece la opción proxy de axios en false para evitar conflictos entre configuraciones.
El proxy HTTP también se puede configurar en axios con las opciones host, port y auth, pero para direcciones HTTPS, usar un agente es más estable. El paquete https-proxy-agent permite conectar proxies HTTP con la misma estructura.
import axios from "axios";
import { SocksProxyAgent } from "socks-proxy-agent";
const agent = new SocksProxyAgent("socks5h://spx_user:spx_pass@198.51.100.23:1080");
const client = axios.create({ httpAgent: agent, httpsAgent: agent, proxy: false, timeout: 20000 });
const { data } = await client.get("https://api.ipify.org?format=json");
console.log(data);¿Qué hay que tener en cuenta al enviar múltiples solicitudes simultáneas?
Si inicias miles de solicitudes a la vez con Promise.all, se abren demasiadas conexiones, lo que sobrecarga tanto el proxy como el sitio objetivo. Establece un límite de solicitudes simultáneas y usa una cola de trabajos para procesar solo esa cantidad. Puedes crear una pequeña función o usar un paquete como p-limit.
Distribuye conexiones simultáneas equitativamente entre proxies para evitar sobrecargar una IP. Los planes compartidos incluyen 500 conexiones simultáneas por IP.
¿Se puede aplicar un proxy a todas las solicitudes mediante variables de entorno?
EnvHttpProxyAgent de undici lee las variables de entorno HTTP_PROXY, HTTPS_PROXY y NO_PROXY para configurar el proxy. Al registrarlo con setGlobalDispatcher, todas las fetch en ese proceso pasarán por el proxy. Es útil cuando múltiples bibliotecas usan fetch internamente.
La configuración global usa la misma IP para todas las solicitudes. Mejor para herramientas que requieren IP fija que para scraping con rotación.
// HTTPS_PROXY=http://spx_user:spx_pass@198.51.100.23:8000 node app.mjs
import { EnvHttpProxyAgent, setGlobalDispatcher, fetch } from "undici";
setGlobalDispatcher(new EnvHttpProxyAgent());
const res = await fetch("https://api.ipify.org");
console.log(await res.text());¿Cómo soluciono problemas frecuentes?
Si parece que el dispatcher se ignora al pasarlo al fetch integrado de Node.js, verifica si también has importado fetch desde el paquete undici. Si las versiones del fetch integrado y del undici instalado son diferentes, el agente puede no coincidir. La forma más segura es importar fetch y ProxyAgent desde el mismo paquete.
La respuesta 407 indica que la información de autenticación es incorrecta. Verifica que después de 'Basic' en el valor 'token' esté el nombre de usuario:contraseña codificado en base64. ECONNREFUSED suele ocurrir cuando el puerto es incorrecto. Usa el puerto HTTP para agentes HTTP y el puerto SOCKS5 para agentes SOCKS.
- fetch y ProxyAgent están en el mismo paquete undici
- 407: Verifica el valor 'token' y las IPs registradas
- ECONNREFUSED: Distinción entre puerto HTTP y SOCKS5
- Límite de tiempo para todas las solicitudes