Solución de problemas6 min de lectura

Variables de entorno de proxy: HTTP_PROXY y NO_PROXY

Diagnostica las diferencias de enrutamiento de HTTP_PROXY, HTTPS_PROXY, ALL_PROXY y NO_PROXY en curl, Python Requests y Node.js con una comprobación local aislada.

En esta página

El punto de partida

Una variable de proxy puede estar presente mientras una petición va directamente a su destino. Revisa el cliente, el esquema del destino y las reglas de exclusión antes de culpar al gateway.

Separa la selección del destino de la conexión al proxy

Estas variables son convenciones que interpreta cada cliente, no un interruptor del sistema operativo que enrute todas las aplicaciones. Que un comando curl funcione no demuestra que otra biblioteca haya heredado o respetado la misma configuración.

El nombre de la variable normalmente selecciona el esquema del destino. HTTPS_PROXY puede contener una URL de proxy http://: el destino usa HTTPS, mientras que el cliente primero se conecta a un proxy HTTP y solicita un túnel CONNECT. El esquema de la URL del proxy describe la conexión con el propio proxy.

  • http_proxy / HTTP_PROXY: eligen un proxy para un destino HTTP, según las reglas de mayúsculas y minúsculas del cliente.
  • https_proxy / HTTPS_PROXY: eligen un proxy para un destino HTTPS.
  • all_proxy / ALL_PROXY: un valor de respaldo en curl y Requests cuando no se selecciona ningún proxy específico aplicable al destino. No asumas que todos los clientes lo implementan.
  • no_proxy / NO_PROXY: solicitan conexiones directas para los destinos coincidentes cuando se aplica la selección de proxy por entorno del cliente. Es una excepción de enrutamiento, no otra dirección de proxy.

Identifica el cliente y la versión que realmente hacen la petición

Las comprobaciones locales de enrutamiento de esta guía usaron curl 8.7.1, Requests 2.34.2 sobre Python 3.14.7 y Node.js 26.8.1. Las notas de la versión 24.5.0 de Node.js documentan por separado la introducción del soporte de proxy por entorno para sus agentes HTTP/HTTPS predeterminados; fetch obtuvo su activación opcional antes, en la 24.0.0. Esas versiones históricas se revisaron en la documentación, no se ejecutaron aquí.

  • curl acepta http_proxy en minúsculas pero ignora deliberadamente HTTP_PROXY en mayúsculas, porque los entornos CGI pueden derivar ese nombre de una cabecera de una petición entrante. HTTPS_PROXY y ALL_PROXY sí se aceptan. Una variable específica de protocolo tiene prioridad sobre ALL_PROXY.
  • Requests normalmente lee las cuatro familias de variables, incluidos los nombres en mayúsculas. Su descubrimiento de proxies en Python prefiere las minúsculas cuando ambas formas discrepan; los entornos CGI con REQUEST_METHOD definido ignoran HTTP_PROXY en mayúsculas. Los valores del entorno pueden sobrescribir session.proxies, así que un diccionario de sesión por sí solo no es un límite de aislamiento.
  • El soporte de entorno integrado en Node requiere activación explícita: inicia el proceso con NODE_USE_ENV_PROXY=1. Los agentes HTTP/HTTPS predeterminados y el fetch nativo usan entonces HTTP_PROXY, HTTPS_PROXY y NO_PROXY; las minúsculas tienen prioridad. ALL_PROXY por sí solo no es un sustituto en este modo integrado. Los agentes personalizados, los dispatchers y los paquetes de terceros necesitan su propia revisión de configuración.

Trata NO_PROXY como un cambio de ruta

Empieza con una lista separada por comas de hosts de destino autorizados exactos y después verifica qué peticiones coinciden realmente. No pegues URL completas ni rutas en una lista de hosts. Un comodín * solicita enrutamiento directo para todos los destinos en los clientes tratados aquí; no sirve como arreglo improvisado para un proxy obligatorio.

No existe una gramática de coincidencia portable que puedas dar por hecha entre estos clientes. Por ejemplo, curl documenta la coincidencia CIDR desde la 7.86.0; la documentación integrada de Node enumera nombres de host, sufijos de dominio, rangos de direcciones y entradas host:port. Una entrada de dominio, un comodín o una subred que funciona en un cliente necesita una prueba aparte en otro. Los alias de DNS y las IP literales también cambian el nombre que se compara.

Si una petición interna usa el proxy de forma inesperada, inspecciona el nombre de host de destino real y las reglas de coincidencia propias del cliente. Si una petición externa va directa de forma inesperada, inspecciona NO_PROXY en ambas formas (mayúsculas y minúsculas) y cualquier configuración explícita de agente. Cambia solo la regla de enrutamiento aprobada para ese despliegue.

Observa la selección de proxy sin un gateway real

Guarda esto como proxy_env_lab.py y ejecuta python3 proxy_env_lab.py con curl instalado. Arranca dos servidores HTTP marcadores en puertos loopback aleatorios: uno representa un destino directo y otro representa el proxy seleccionado. El marcador del proxy responde localmente; no reenvía tráfico ni prueba CONNECT, TLS, autenticación ni una IP de salida.

Cada proceso curl recibe únicamente el entorno de prueba suministrado. --disable es su primera opción, de modo que un curlrc personal no pueda alterar el experimento. Los resultados esperados son proxy, direct, direct, proxy. Esto demuestra que incluso un --proxy explícito sigue sujeto a NO_PROXY, a menos que la lista de exclusión se vacíe explícitamente para este diagnóstico local.

proxy_env_lab.py · biblioteca estándar de Python 3 + curl

python
import shutil
import subprocess
import threading
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer

curl = shutil.which("curl")
if not curl:
    raise SystemExit("Install curl before running this local lab")

def marker(label):
    class Handler(BaseHTTPRequestHandler):
        def do_GET(self):
            body = label.encode()
            self.send_response(200)
            self.send_header("Content-Length", str(len(body)))
            self.end_headers()
            self.wfile.write(body)

        def log_message(self, *_):
            pass

    server = ThreadingHTTPServer(("127.0.0.1", 0), Handler)
    threading.Thread(target=server.serve_forever, daemon=True).start()
    return server, "http://127.0.0.1:" + str(server.server_port)

origin, destination = marker("direct")
proxy_server, proxy = marker("proxy")
cases = [
    ("environment", {"http_proxy": proxy}, [], "proxy"),
    ("host bypass", {"http_proxy": proxy, "NO_PROXY": "127.0.0.1"}, [], "direct"),
    ("explicit + bypass", {"NO_PROXY": "127.0.0.1"}, ["--proxy", proxy], "direct"),
    ("explicit + cleared bypass", {"NO_PROXY": "127.0.0.1"},
     ["--proxy", proxy, "--noproxy", ""], "proxy"),
]
try:
    for label, environment, options, expected in cases:
        result = subprocess.run(
            [curl, "--disable", "--silent", "--show-error", "--fail",
             "--connect-timeout", "2", "--max-time", "3", *options, destination],
            env=environment, capture_output=True, text=True, timeout=5,
        )
        if result.returncode or result.stdout != expected:
            raise SystemExit("Local route check failed: " + label)
        print(label + ": " + result.stdout)
finally:
    for server in (origin, proxy_server):
        server.shutdown()
        server.server_close()

Haz deliberada la configuración base de la aplicación

Para un diagnóstico de curl con proxy obligatorio, usa un --proxy explícito junto con --noproxy '', como muestra la guía existente de configuración de curl. Para Requests, pasa el argumento proxies en la petición; usa una Session dedicada con trust_env=False cuando la configuración base deba ignorar los ajustes de proxy heredados. Eso también desactiva la autenticación y la configuración del paquete de CA derivadas del entorno, así que, cuando haga falta, proporciona explícitamente un paquete de confianza personalizado aprobado.

Para Node, elige entre una activación por entorno revisada o un agente/dispatcher compatible explícito. No asumas que el agente personalizado de una biblioteca hereda la configuración global. Confirma el runtime exacto que usa el worker o el servicio, no solo la versión instalada en una terminal interactiva.

Una vez entendida la regla local, compara un destino autorizado con un gateway documentado por el proveedor. Mantén la verificación TLS activada, usa un plazo límite y confirma la ruta de la petición con observaciones controladas en el endpoint o en el proxy. Un estado HTTP de éxito por sí solo no establece qué ruta se tomó.

Registra decisiones sin registrar secretos

Compara el entorno del proceso que hace la petición con el de su lanzador, contenedor o configuración de servicio. Registra los nombres de las variables y si están definidas, la versión del cliente, el esquema del destino y la ruta observada. Evita volcados del entorno completo, trazas de shell, registros de protocolo detallados y texto de excepciones en bruto: una URL de proxy puede contener una contraseña.

Haz que un gestor de secretos aprobado rellene de forma privada las credenciales necesarias en el entorno del proceso. No las pegues en un comando, no confirmes un archivo de entorno en el repositorio ni envíes configuración en bruto al soporte. Las variables de entorno son un mecanismo de entrega, no una bóveda de secretos. Este laboratorio local no usa credenciales y no dice nada sobre la disponibilidad del servicio de ipvolt.

Antes de publicar

  • Identifica el cliente real, la versión del runtime y el esquema del destino.
  • Revisa los nombres de variables en mayúsculas y en minúsculas sin registrar sus valores.
  • Verifica la coincidencia de NO_PROXY y la ruta seleccionada en un destino autorizado.
  • Compara un entorno controlado con una configuración explícita antes de añadir reintentos.

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.