El punto de partida
Usa un dispatcher para una sola llamada a fetch, de modo que el proxy sea explícito y los demás clientes de red de tu proceso conserven su propia configuración.
Elige un runtime y un dispatcher compatibles
Este ejemplo del lado del servidor está pensado para Node.js 24 y Undici 7. En un proyecto de ejemplo aparte, ejecuta npm install undici@7. El fetch integrado de Node acepta un dispatcher compatible con Undici. Esto es distinto del fetch del navegador, donde el JavaScript de la página no puede seleccionar un proxy del sistema arbitrario.
Usa un gateway HTTP(S) documentado por tu proveedor en PROXY_URL, por ejemplo el https://proxy.example.invalid:8443 que a propósito no funciona. Inyecta PROXY_USERNAME y PROXY_PASSWORD a través del gestor de secretos de tu despliegue. No pongas credenciales del gateway en código de frontend.
Mantén la autenticación del proxy en el dispatcher
Guárdalo como proxy-check.mjs y ejecuta node proxy-check.mjs. El token Basic pertenece al agente del proxy, no a la cabecera Authorization del destino. La petición tiene un plazo de cancelación de 30 segundos y no sigue redirecciones automáticamente.
proxy-check.mjs · Node.js 24 + Undici 7
import { ProxyAgent } from 'undici';
function required(name) {
const value = process.env[name];
if (!value) throw new Error('Missing environment variable: ' + name);
return value;
}
const gateway = new URL(required('PROXY_URL'));
if (!['http:', 'https:'].includes(gateway.protocol) || gateway.username || gateway.password) {
throw new Error('Use a credential-free HTTP(S) proxy URL');
}
const credentials = required('PROXY_USERNAME') + ':' + required('PROXY_PASSWORD');
const dispatcher = new ProxyAgent({
uri: gateway.href,
token: 'Basic ' + Buffer.from(credentials).toString('base64'),
});
try {
const response = await fetch('https://example.com/', {
dispatcher,
signal: AbortSignal.timeout(30_000),
redirect: 'manual',
});
await response.body?.cancel();
if (!response.ok) throw new Error('Destination HTTP ' + response.status);
console.log({ status: response.status });
} finally {
await dispatcher.close();
}Pasa de un diagnóstico a un worker
Este script cancela a propósito el cuerpo de la respuesta porque el diagnóstico solo necesita un estado. Un worker que use el resultado debería consumir el cuerpo dentro de su presupuesto. Consúmelo o cancélalo siempre antes de continuar, y cierra el dispatcher cuando el worker se apague.
Para trabajos repetidos, reutiliza un dispatcher con concurrencia acotada en lugar de crear uno por petición. Mantén el plazo de cada trabajo por separado. No instales un dispatcher a nivel de proceso a menos que cambiar el enrutamiento de todos los clientes afectados sea intencional.
- Un error HTTP es una respuesta; fetch no rechaza la promesa solo porque su estado sea 4xx o 5xx.
- Un rechazo del CONNECT puede aparecer como un fallo de fetch antes de que exista una Response del destino.
- Captura categorías de error sin datos sensibles; no registres el token Basic ni URLs completas con credenciales.
Antes de pasar a producción
- Usa las versiones mayores documentadas de Node y Undici.
- Pasa el dispatcher de forma explícita.
- Consume o cancela los cuerpos de respuesta y cierra el agente.
Fuentes y lecturas adicionales
Referencias técnicas usadas para esta guía. Consulta la documentación de tu versión instalada y la configuración compatible de tu proveedor.