Análisis21 min de lectura

Comodín, punto inicial y CIDR en NO_PROXY: una matriz probada

¿Qué entradas de NO_PROXY significan lo mismo en curl, Python, Node, Go y wget? Una prueba en loopback de *, puntos iniciales, CIDR, IPv6 y puertos, con datos en bruto.

En esta página

NO_PROXY no tiene una gramática estándar, y los clientes que la leen discrepan en casi todo. En la matriz principal de un laboratorio en loopback ejecutado el 23 de septiembre de 2026, 15 clientes recibieron valores idénticos de no_proxy y NO_PROXY: dos builds de curl, wget, Go, cinco clientes de Python y seis configuraciones de Node.js. De las 49 combinaciones de valor y petición de la matriz principal, 21 se enrutaron igual en todos los clientes y 28 no. Cada celda está en el results.csv publicado, una fila por cliente y petición.

Si un solo valor tiene que servir para todos, usa únicamente las formas que se comportaron de manera idéntica en todas partes y da el mismo valor a los dos nombres de variable:

sh
# example.com and 192.0.2.10 stand for your own domain and address
export no_proxy="localhost,127.0.0.1,example.com,.example.com,192.0.2.10"
export NO_PROXY="$no_proxy"
  • El par example.com,.example.com (probado como example.test,.example.test) fue la única escritura que cubrió un dominio y todos sus subdominios en los 15 clientes. Nunca coincidió con notexample.test.
  • Una dirección IPv4 exacta coincidió solo consigo misma en todos los clientes. Un rango CIDR IPv4 funcionó solo en 4 de los 15.
  • localhost,127.0.0.1 evitó el proxy para 127.0.0.1 y localhost en todos los clientes. Sin entradas de loopback, solo Go se saltó el proxy para loopback.
  • wget solo lee no_proxy. El net/http de Go 1.27.1 prefiere NO_PROXY cuando ambas están definidas, y está previsto que Go 1.28 prefiera no_proxy. Mantén los dos nombres idénticos.

El conjunto de datos publicado prueba estas entradas en casos separados. En una ejecución puntual de la línea exacta de arriba, con example.test en lugar de example.com, cada una de sus nueve peticiones de prueba también se enrutó igual en los 15 clientes.

Dos tipos de escritura dan problemas. Algunas rompen un cliente o desactivan una lista. Una entrada IPv6 entre corchetes hace que httpx y httpx2 fallen al crear el cliente, y lo mismo ocurre con una entrada CIDR IPv6 en httpx 0.28.1. Un no_proxy en minúsculas vacío junto a un NO_PROXY definido desactivó la lista en 9 clientes. Otras son ignoradas en silencio por algunos clientes: *.example.com por 8, * dentro de una lista por 9, un rango CIDR IPv4 por 11, un punto final por 11, una lista separada por espacios por 9, y una entrada host:port por entre 4 y 6, según su forma. Incluso un * solo es ignorado por wget.

Estos resultados son sintéticos. Proceden de un solo Mac, un proxy en 127.0.0.1, nombres .test reservados y direcciones IP de documentación, y describen cómo eligen una ruta las versiones fijadas de los clientes, no cómo se comporta ningún servicio de proxy. Para saber cómo lee cada cliente las variables de proxy en primer lugar, consulta cómo leen los clientes HTTP_PROXY y NO_PROXY. Si el fetch() de Node ignora el proxy por completo, empieza por Corregir «TypeError: fetch failed» de Node.js detrás de un proxy.

El subconjunto portable y las escrituras que evitar

Las tablas usan los nombres que solicitó el laboratorio: example.test, sub.example.test, a.b.example.test y notexample.test. Ningún cliente de aquí resuelve un nombre de host antes de decidir: su código fuente compara los nombres como texto, y curl, Go y Requests analizan una dirección IP presente en la URL para las comprobaciones CIDR. Ninguna de las 662 peticiones enviadas por proxy provocó una denegación registrada del sandbox por una consulta DNS o una conexión, aunque el registro del kernel descarta algunas líneas de denegación. Por tanto, los nombres de dominio reales deberían comportarse como estos.

Los 15 clientes, cada uno leyendo las variables de proxy del entorno con la configuración indicada, fueron:

  • curl 8.7.1 (macOS) y curl 8.22.0;
  • GNU Wget 1.25.0;
  • net/http de Go 1.27.1 con http.ProxyFromEnvironment;
  • urllib de Python 3.14.7, requests 2.34.2, httpx 0.28.1, httpx2 2.13.1 y aiohttp 3.14.3 con trust_env=True;
  • fetch() y http.get() integrados de Node.js 26.10.0 y 24.21.0, cada uno con NODE_USE_ENV_PROXY=1 (llamados «Node 26 fetch», «Node 26 http», etc. más abajo);
  • fetch() de undici 7.29.1 y 8.11.0 de npm con dispatcher: new EnvHttpProxyAgent(), ambos en Node 26.10.0.

Estas entradas dieron el mismo resultado en los 15 clientes para las peticiones indicadas:

EntradaResultado en todos los clientes
example.testEvitó el proxy para example.test; notexample.test siguió usando el proxy (los subdominios difirieron en el cliente http de Node)
.example.testEvitó el proxy para sub.example.test y a.b.example.test (el apex difirió en 4 clientes)
example.test,.example.testEvitó el proxy para el apex y ambos subdominios; notexample.test siguió usando el proxy
192.0.2.10Evitó el proxy para esa dirección; 192.0.2.11 siguió usando el proxy
EXAMPLE.TEST, o una petición a EXAMPLE.TESTLas mayúsculas y minúsculas no cambiaron nada
other.test , example.testUn espacio después de una coma no cambió nada
other.test,,example.test u other.test,,Los elementos vacíos no coincidieron con nada
localhost,127.0.0.1,::1Evitó el proxy para 127.0.0.1 y localhost ([::1] difirió; ver la sección sobre loopback)
no_proxy definida, NO_PROXY sin definirTodos los clientes leyeron la variable en minúsculas

Cada escritura que evitar de la respuesta anterior tiene su propia sección más abajo, con los clientes que divergieron.

Comodín en NO_PROXY: ¿*.example.com coincide con example.com?

Depende del cliente, y 8 de 15 no lo trataron como comodín en absoluto. Con NO_PROXY=*.example.test:

PeticiónEvitaron el proxyUsaron el proxy
example.testNode 26 fetch, Node 24 fetch, undici 7.29.1Los otros 12
sub.example.test, a.b.example.testGo, fetch y http de Node 26 y 24, undici 7.29.1 y 8.11.0curl (ambos), wget, urllib, requests, httpx, httpx2, aiohttp
notexample.testNingunoLos 15

curl, wget y los cinco clientes de Python no coincidieron con nada con esta entrada. Go, el cliente http de Node y undici 8.11.0 la leyeron como «solo subdominios», y el fetch() integrado de Node y undici 7.29.1 también evitaron el proxy para el apex.

La discrepancia sobre el apex entre el fetch() integrado de Node 26 y undici 8.11.0 de npm es nueva. El PR #5777 de undici, publicado en undici 8.11.0 el 22 de septiembre de 2026, dice que una entrada *.domain «solo debe coincidir con sub.example.com / a.b.example.com, no con el apex». Node 26.10.0 todavía incluye undici 8.10.2. Así que, en el mismo runtime Node 26.10.0, el fetch() integrado evitó el proxy para example.test y undici 8.11.0 de npm lo envió al proxy. El mismo pull request hizo que * funcionara dentro de una lista o con espacios alrededor, lo que explica las únicas otras filas en las que esos dos clientes difirieron.

Para los subdominios en todos los clientes, escribe .example.com. Añade example.com si el apex también debe evitar el proxy. Ninguna escritura probada mantuvo el apex en el proxy en los 15 clientes mientras sus subdominios lo evitaban.

Punto inicial frente a dominio sin punto: .example.com, example.com y subdominios

Entrada y peticiónEvitaron el proxyUsaron el proxy
example.test, petición a sub.example.test13 clientesNode 26 http, Node 24 http
.example.test, petición a example.test11 clienteswget, Go, httpx, httpx2

Un dominio sin punto coincidió con sus subdominios en todas partes salvo en el cliente http integrado de Node, que coincidió con example.test de forma exacta. El laboratorio reprodujo node#65616, notificado por primera vez en Node 24.18.1, en 26.10.0 y 24.21.0. La propia lista de formatos de NO_PROXY de Node llama a example.com «coincidencia exacta del nombre de host», lo que describe a http.request(), no a fetch(). Una corrección, node#65617, seguía abierta el 23 de septiembre de 2026.

Un punto inicial coincidió con todos los subdominios en todos los clientes, pero wget, Go, httpx y httpx2 no lo aplicaron al apex. Go lo documenta: un nombre de dominio con punto inicial «coincide solo con subdominios» (httpproxy).

Ninguna de las dos entradas coincidió con notexample.test en ningún cliente. Requests solo aplica ese límite de etiqueta desde la 2.34.0; sus notas de la versión dicen que «ya no realiza coincidencias voraces en los dominios de no_proxy». No se probaron versiones anteriores de Requests.

NO_PROXY=* y * dentro de una lista

ValorEvitaron el proxyUsaron el proxy
*14 clienteswget
other.test,* o " * "Go, httpx, httpx2, Node 26 http, Node 24 http, undici 8.11.0curl (ambos), wget, urllib, requests, aiohttp, Node 26 fetch, Node 24 fetch, undici 7.29.1

* funciona como valor completo, sin espacios, en todos los clientes salvo wget. curl lo documenta así: «El único comodín disponible es un solo carácter *» (CURLOPT_NOPROXY). wget 1.25.0 no tiene comodín. Para que un comando wget concreto no pase por el proxy, usa su opción --no-proxy (manual de wget).

CIDR, rangos de IP y subredes en NO_PROXY

Con NO_PROXY=192.0.2.0/24, una petición a 192.0.2.10 evitó el proxy en curl 8.7.1, curl 8.22.0, Go y requests. Los otros 11 la enviaron al proxy: wget, urllib, httpx, httpx2, aiohttp y los seis clientes de Node. 198.51.100.10, fuera del rango, usó el proxy en todos los clientes. Ningún cliente lanzó un error por el rango IPv4; los 11 sin soporte lo ignoraron.

  • curl añadió soporte de CIDR en la 7.86.0 (changelog).
  • Requests comprueba CIDR en su propio comparador antes de recurrir a urllib, y por eso los dos clientes de Python difieren.
  • httpx y httpx2 aceptaron la entrada sin hacer coincidir el rango. El PR #1165 de httpx2, que añadiría rangos CIDR, estaba abierto el 23 de septiembre.
  • La lista de formatos de NO_PROXY de Node documenta un rango con guion como 192.168.1.1-192.168.1.100 en lugar de CIDR. El laboratorio no probó rangos con guion. Un comentario en node#57872 informa de que un rango con guion funcionó en node:http pero no en fetch() en una versión preliminar de la v27.

Para mantener una subred fuera del proxy en un stack mixto:

  • Enumera las direcciones exactas a las que llaman tus clientes. Las direcciones IPv4 exactas fueron la única forma que coincidió en los 15.
  • Un rango IPv4 puede añadirse junto a ellas para curl, Go y Requests. Con 192.0.2.10,192.0.2.0/24, una ejecución puntual fuera del conjunto de datos publicado evitó el proxy para 192.0.2.10 en los 15 clientes y para 192.0.2.11 solo en esos 4; ningún cliente lanzó un error. Deja fuera los rangos IPv6, porque httpx 0.28.1 falla con ellos (ver más abajo).
  • Un rango se aplica solo a una dirección IP escrita en la URL. curl, Go y Requests no resuelven primero un nombre de host, así que 192.0.2.0/24 no cubre un nombre que se resuelva dentro de él. Esto procede de su código fuente; el laboratorio usó únicamente direcciones IP.

IPv6 en NO_PROXY: sin corchetes, entre corchetes y CIDR

Para una petición a http://[2001:db8::10]/:

EntradaEvitaron el proxyNo lo evitaron
2001:db8::1012 clientesurllib, Node 24 fetch y undici 7.29.1 usaron el proxy
[2001:db8::10]urllib, Node 26 fetch, Node 24 fetch, undici 7.29.1, undici 8.11.08 usaron el proxy; httpx y httpx2 lanzaron un error
2001:db8::/48curl 8.22.0, Go12 usaron el proxy; httpx lanzó un error
[2001:db8::10]:8080, petición al puerto 8080Go, urllib, Node 26 fetch, Node 24 fetch, undici 7.29.1, undici 8.11.07 usaron el proxy; httpx y httpx2 lanzaron un error

La forma sin corchetes fue la que mejor funcionó: coincidió en 12 clientes y no rompió ninguno. curl la pide así: «Introduce las direcciones IPv6 numéricas en la lista de nombres de host sin corchetes» (CURLOPT_NOPROXY). undici añadió la coincidencia de IPv6 sin corchetes en la 8.10.0 (PR #5623); undici 7.29.1, que Node 24.21.0 incluye, no la tiene.

De las dos builds de curl, solo la 8.22.0 hizo coincidir el rango IPv6. Antes de la 8.18.0 (curl#19828), el comparador de curl trataba un host como IPv6 solo cuando llegaba entre corchetes, cosa que nunca ocurre, así que una entrada CIDR IPv6 no podía coincidir.

InvalidURL: Invalid port de httpx por una entrada IPv6 entre corchetes

En httpx 0.28.1 y httpx2 2.13.1, una entrada entre corchetes es peor que ignorada. Crear un cliente con el manejo del entorno por defecto lanzó InvalidURL: Invalid port: 'db8::10]' antes de enviar ninguna petición. Con NO_PROXY=localhost,127.0.0.1,[::1], el error fue Invalid port: ':1]', y las peticiones a 127.0.0.1 y localhost también fallaron, porque el cliente nunca llegó a crearse. httpx 0.28.1 lanzó el mismo error para 2001:db8::/48 (Invalid port: 'db8::'). httpx2 2.5.0 eliminó ese fallo («Allow IPv6 CIDR notation in no_proxy», changelog), pero, como muestra la tabla, siguió sin hacer coincidir el rango.

Para corregirlo, escribe las entradas IPv6 sin corchetes, como ::1 o 2001:db8::10; ambos clientes se crearon con normalidad con esas formas. Mantén los rangos IPv6 fuera de cualquier valor que lea httpx 0.28.1. Si no puedes cambiar la variable, crea ese cliente con trust_env=False, que también se creó con normalidad, y pásale el proxy en el código, como hace la guía de proxy para HTTPX. Ese cliente ignora entonces todas las variables de proxy.

Puertos en NO_PROXY: host:port

Para una petición al puerto 8080 indicado en la entrada:

EntradaEvitaron el proxyUsaron el proxy
example.test:808011 clientescurl (ambos), wget, aiohttp
192.0.2.10:808010 clientescurl (ambos), wget, requests, aiohttp
.example.test:80809 clientescurl (ambos), wget, aiohttp, Node 26 http, Node 24 http

En el puerto 80, las tres entradas usaron el proxy en todos los clientes, así que ningún cliente aplicó una entrada con puerto a un puerto distinto. El fallo fue el contrario: algunos clientes nunca hicieron coincidir la entrada. La documentación de curl no describe ninguna sintaxis de puerto para las entradas, y wget compara solo el nombre de host. aiohttp pasa el host sin su puerto al comparador de urllib (código fuente). Requests nunca hizo coincidir una entrada IPv4 que incluyera un puerto: con 192.0.2.10:8080, usó el proxy tanto en el puerto 8080 como en el 80. Su comprobación de IPv4 compara solo el host, según el PR #7586, una corrección que seguía abierta el 23 de septiembre.

Mantén los puertos fuera de un valor compartido. Si un puerto debe evitar el proxy, configura esa exclusión en el cliente que la necesita.

Espacios, elementos vacíos, puntos finales y mayúsculas

  • Espacio antes de una coma: con example.test ,other.test, wget conservó el espacio final como parte de la entrada y envió example.test al proxy. Los otros 14 evitaron el proxy.
  • Espacios en lugar de comas: con other.test example.test, curl 8.7.1, Node 26 fetch, Node 24 fetch, undici 7.29.1 y undici 8.11.0 evitaron el proxy para ambos hosts. curl 8.22.0 lo evitó solo para other.test. Los otros 9 no lo evitaron para ninguno. curl 8.9.0 introdujo el cambio: «noproxy: patterns need to be comma separated» (changelog).
  • Elementos vacíos: other.test,, no evitó el proxy para example.test en ningún cliente, así que ningún cliente trató un elemento vacío como «coincide con todo».
  • Puntos finales: una entrada example.test., o una petición a http://example.test./, coincidió solo en curl 8.7.1, curl 8.22.0, Node 26 fetch y undici 8.11.0. undici lo añadió en la 8.10.1 (PR #5637), así que Node 24 fetch y undici 7.29.1 no lo tienen.
  • Mayúsculas: las entradas y los hosts en mayúsculas coincidieron en los 15 clientes.

NO_PROXY frente a no_proxy: cuál gana y la trampa de la variable vacía

Para una petición a example.test:

EntornoEvitaron el proxyUsaron el proxy
Solo NO_PROXY=example.test14 clienteswget
Solo no_proxy=example.testLos 15Ninguno
NO_PROXY=example.test, no_proxy=other.testGo 1.27.1Los otros 14
NO_PROXY=example.test, no_proxy=""curl (ambos), Go, requests, Node 26 http, Node 24 httpwget, urllib, httpx, httpx2, aiohttp, Node 26 fetch, Node 24 fetch, undici 7.29.1, undici 8.11.0

Cuando los dos nombres discrepan, el net/http de Go 1.27.1 usa NO_PROXY y los otros 14 usan no_proxy. Una variable en minúsculas vacía divide a los clientes 6 a 9. curl, Go, requests y el cliente http de Node la tratan como no definida y recurren a NO_PROXY. Los otros nueve la tratan como una lista vacía. node#66202, abierto el 22 de septiembre de 2026 contra una build preliminar, describe esta división entre fetch() y http.request(). El laboratorio la reprodujo en las versiones publicadas Node 26.10.0 y 24.21.0.

La precedencia de Go está cambiando. El commit a02ddfa7ea de golang.org/x/net, hecho para golang/go#79656 y publicado en x/net v0.58.0, hace que el paquete httpproxy prefiera los nombres en minúsculas, y el borrador de las notas de la versión Go 1.28 recoge el mismo cambio para ProxyFromEnvironment. En una comprobación aparte, x/net v0.58.0 y v0.59.0 ya seguían no_proxy en el caso de conflicto. Go sigue omitiendo los valores vacíos, así que sus resultados con la variable vacía no cambian.

La variable del proxy tiene la misma trampa. Con http_proxy="" y HTTP_PROXY definida, solo Go, Node 26 http y Node 24 http usaron el proxy; los otros 12 fueron directos. curl lee http_proxy solo en minúsculas.

Da el mismo valor a los dos nombres, y elimina una variable con unset en lugar de exportarla vacía.

localhost, 127.0.0.1 y ::1: solo Go se salta el proxy por su cuenta

Sin ningún NO_PROXY, Go fue directo para 127.0.0.1, localhost y [::1]. Su paquete httpproxy documenta que localhost y las direcciones de loopback nunca usan un proxy. Los otros 14 clientes enviaron las tres al proxy. Con un proxy remoto configurado, esos clientes enviarían por tanto a ese proxy una petición destinada a un servidor de desarrollo local, a menos que loopback esté en la lista.

Valor127.0.0.1 y localhost[::1]
localhost,127.0.0.1,::1Evitado en los 15 (Go: regla de loopback integrada)Evitado en 12 (Go: regla de loopback integrada); urllib, Node 24 fetch y undici 7.29.1 usaron el proxy
localhost,127.0.0.1,[::1]httpx y httpx2 lanzaron un error; 13 lo evitaron (Go: regla de loopback integrada)Evitado en urllib, Node 26 fetch, Node 24 fetch, undici 7.29.1 y 8.11.0, y en Go por su regla de loopback integrada; httpx y httpx2 lanzaron un error; 7 usaron el proxy

Las exclusiones de Go en esta tabla proceden de su regla integrada, no de la lista, así que no demuestran que [::1] funcione como entrada en Go; en la matriz principal, Go usó el proxy para la entrada entre corchetes [2001:db8::10].

Incluye localhost,127.0.0.1. Añade un ::1 sin corchetes si usas loopback IPv6, y nunca [::1].

¿HTTPS a través de CONNECT recibe la misma decisión de exclusión?

Sí, en esta ejecución. Para cada una de las 8 URL HTTPS, todos los clientes tomaron la misma decisión que para la URL HTTP correspondiente: 120 de 120 comparaciones. Todas las peticiones HTTPS enviadas por proxy llegaron al proxy como CONNECT.

Node.js: NO_PROXY no funciona con fetch ni con http.request

Cuatro cosas distintas pueden hacer que NO_PROXY parezca roto en Node. El caso del ProxyAgent explícito procede de la documentación de undici y no se probó; los otros tres son resultados del laboratorio.

La activación. Sin NODE_USE_ENV_PROXY=1 ni --use-env-proxy (documentación), fetch() y http.get() de Node 26 y 24 ignoraron las variables de proxy y fueron directos en las 36 celdas de control. La guía sobre fetch de Node cubre ese fallo.

Un ProxyAgent explícito. La documentación de undici describe un ProxyAgent como algo que enruta todas las peticiones a través de su proxy y no menciona ninguna lista de exclusión; leer no_proxy y NO_PROXY es lo que añade EnvHttpProxyAgent (documentación de undici 8.11.0 de ProxyAgent y EnvHttpProxyAgent). El código que pasa un ProxyAgent como dispatcher, como hace la guía de configuración de proxy para fetch de Node.js, ignora por tanto NO_PROXY. Para conservar una lista de exclusión, usa new EnvHttpProxyAgent(), que lee las variables o acepta las opciones httpProxy, httpsProxy y noProxy.

Dos comparadores en el mismo runtime. http.request() usa el comparador propio de Node, y el fetch() integrado usa el EnvHttpProxyAgent de undici. Discreparon en 20 de las 72 filas no base en Node 26.10.0 y en 19 de 72 en Node 24.21.0. El comparador de Node lleva el comentario TODO(joyeecheung): share code with undici. (código fuente), y compartir ese código sigue siendo un punto abierto en el issue de seguimiento node#57872.

undici incluido frente a undici de npm. En Node 26.10.0, el fetch() integrado (undici 8.10.2) y undici 8.11.0 de npm difirieron en 4 filas, todas casos de comodín. El fetch() de Node 24.21.0 y undici 7.29.1 de npm no difirieron en absoluto. undici 7.29.1 y 8.11.0 difirieron en 11 filas.

La tabla siguiente muestra una fila por cada tipo de diferencia en Node 26.10.0, con los valores de las secciones anteriores. Las otras 12 de las 20 filas discrepantes repiten estos tipos para otros hosts y peticiones, más el caso http_proxy="".

Diferenciafetchhttpundici 8.11.0
Dominio sin punto, petición a un subdominioEvitadoProxyEvitado
*.example.test, petición al apexEvitadoProxyProxy
* dentro de una listaProxyEvitadoEvitado
Dirección IPv6 entre corchetesEvitadoProxyEvitado
Punto inicial con un puertoEvitadoProxyEvitado
Entrada con punto finalEvitadoProxyEvitado
Espacios en lugar de comasEvitadoProxyEvitado
no_proxy vacío, NO_PROXY definidoProxyEvitadoProxy

Node 24.21.0 mostró el mismo patrón, con dos excepciones. Para el punto final, sus dos clientes usaron el proxy. Para una entrada IPv6 sin corchetes, solo http evitó el proxy.

Un comentario del 1 de septiembre de 2026 en node#57872 ya comparó node:http con fetch() en una build preliminar de la v27.0.0. Las filas de Node del laboratorio coinciden con las 8 filas de esa tabla que pudo comparar.

Lo que GitLab encontró en 2021 y lo que cambió

El artículo de 2021 de Stan Hu en GitLab, con investigación de Nourdin el Bacha, comparó curl, wget, Ruby, Python, Go y Java a partir de su código fuente y su documentación. Recomendó un mínimo común denominador y propuso un estándar.

De las 32 celdas de su tabla de no_proxy que este laboratorio pudo comprobar (8 filas para curl, wget, Python mediante urllib y Go), 31 siguen coincidiendo. La excepción es curl y CIDR: la tabla de 2021 dice que no, pero ambas builds de curl lo respetaron aquí. La celda de precedencia «Uppercase» de Go sigue coincidiendo con Go 1.27.1 y está previsto que cambie en Go 1.28. El laboratorio no pudo comprobar las filas «Supports regexes?» y «Resolves IP addresses?»; la segunda marca Go como «Yes», pero el propio texto del artículo dice que solo Ruby resuelve nombres de host, y el código fuente de Go 1.27.1 no lo hace. Ruby y Java no se probaron.

Su consejo del mínimo común denominador se sostiene solo en parte. Un no_proxy solo en minúsculas, las comas y las direcciones IPv4 exactas funcionaron en los 15 clientes; un punto inicial siguió sin cubrir el apex en 4, como advertía el artículo, y solo 4 respetaron CIDR IPv4, así que evitar CIDR sigue siendo sensato. «Los sufijos siempre coinciden» falló en el cliente http de Node, los «valores hostname:port separados por comas» coincidieron solo en entre 9 y 11 clientes por entrada (8 coincidieron con las tres), y ninguna forma de IPv6 dio el mismo resultado en todos los clientes.

Varios de estos comportamientos son recientes. Requests 2.34.0 (11 de mayo de 2026) añadió el límite de etiqueta. httpx2 2.5.0 (25 de junio de 2026) dejó de fallar con las entradas CIDR IPv6, sin hacerlas coincidir. undici 8.10.0, 8.10.1 y 8.11.0 (agosto y septiembre de 2026) añadieron IPv6 sin corchetes, los puntos finales y las nuevas reglas de comodín.

Cómo funciona la prueba y cómo repetirla

Un pequeño proxy HTTP en 127.0.0.1 registró cada petición en forma absoluta y cada CONNECT, la respondió por sí mismo y no reenvió nada. Cada petición se ejecutó en su propio proceso bajo sandbox-exec de macOS, con un perfil que deniega todo el tráfico saliente salvo loopback, de modo que un intento directo fallaba en la máquina y no llegaba a ningún host real. Una celda cuenta como enviada por proxy solo cuando el proxy registró su petición; que el cliente recibiera o no una respuesta nunca decidió nada. Sin ningún NO_PROXY definido, los 15 clientes enviaron por proxy todas las formas de URL.

La ejecución publicada, el 23 de septiembre de 2026 de 19:13 a 19:15 UTC, tiene 1.266 celdas. Una repetición limpia que siguió los pasos de abajo a partir del archivo publicado clasificó las 1.266 celdas de forma idéntica.

Para leer la matriz publicada sin ejecutar nada, extrae el archivo y ejecuta python3 no_proxy_lab.py table results.json. No necesita configuración ni red, e imprimió la misma salida en Python 3.9.6 que en 3.14.7. Una repetición completa necesita macOS, Python 3.14 y Go, unos 700 MB de disco y acceso a nodejs.org, pypi.org y registry.npmjs.org durante setup, que no instala nada a nivel del sistema:

sh
curl -fLO https://ipvolt.com/downloads/no-proxy-matching-tested/no-proxy-matching-tested.zip
unzip no-proxy-matching-tested.zip
cd no-proxy-matching-tested
python3.14 no_proxy_lab.py setup --work ./work
python3.14 no_proxy_lab.py run --work ./work --out ./out
python3.14 no_proxy_lab.py compare results.json out/results.json
python3.14 no_proxy_lab.py table out/results.json --section main

compare sale con 0 solo cuando todas las celdas tienen la misma clasificación. El README explica su salida con otras builds de clientes, cómo probar otras versiones o tu propio valor de NO_PROXY, el modo --isolation none para otros sistemas y todos los campos de results.json.

Descargas:

Límites

  • Una sola máquina macOS 15.7.4 arm64, una sola fecha y las versiones indicadas arriba. Cualquier versión nueva puede cambiar una fila: Node 26.10.0 todavía no incluye undici 8.11.0, y está previsto que Go 1.28 cambie la fila de las variables en conflicto.
  • Linux no se probó. Cada cliente compara NO_PROXY en su propio código o en la biblioteca estándar de su lenguaje, y con las variables de proxy definidas, el urllib de Python sigue la misma ruta de entorno en macOS que en Linux. Por tanto, en Linux las filas deberían depender de la versión del cliente y no del sistema operativo. Es una inferencia a partir del código fuente, no un resultado en Linux; el README da ejemplos.
  • La matriz probó solo el manejo de las variables de entorno, con la configuración indicada arriba, y solo el proxy de reenvío HTTP. No probó opciones de proxy explícitas, otros agentes o dispatchers, la configuración de proxy del sistema, Windows, SOCKS, archivos PAC, redirecciones ni autenticación de proxy. Para una respuesta 407, consulta cómo corregir el error de proxy 407.
  • No se probaron los rangos con guion, las direcciones parciales como 192.168.*, las entradas que dependen de la resolución DNS, ni Ruby, Java, .NET, Deno y Bun; el README enumera el resto.
  • Las dos comprobaciones puntuales de arriba se ejecutaron el 24 de septiembre de 2026 con el mismo harness y los mismos clientes. No están en results.json; el README muestra cómo repetirlas.

ipvolt ejecutó estas comprobaciones en local el 23 y el 24 de septiembre de 2026 con las versiones de cliente indicadas arriba. No se probó ningún proveedor externo, servicio de proxy ni red, y nada de lo aquí expuesto describe el servicio de ipvolt.

Si quieres enterarte cuando se abra el acceso a ipvolt, únete a la lista de acceso anticipado. Un correo cuando se abra el acceso. Nada más.

Fuentes

  1. GitLab (2021): We need to talk: Can we standardize NO_PROXY?
  2. nodejs/node#57872: node:http vs fetch() NO_PROXY table (comment, 2026-09-01)
  3. nodejs/node#65616: NO_PROXY=example.com and http.request() subdomains
  4. nodejs/node#65617: http: match subdomains for plain NO_PROXY entries
  5. nodejs/node#66202: fetch() and http.request() with an empty lowercase variable
  6. Node.js 26.10.0 docs: NO_PROXY format
  7. Node.js 26.10.0 docs: NODE_USE_ENV_PROXY
  8. Node.js 26.10.0 source: lib/internal/http.js (http.request matcher)
  9. Node.js 26.10.0: bundled undici version
  10. undici PR #5777: NO_PROXY wildcard semantics
  11. undici v8.11.0 release notes
  12. undici PR #5623: match bare IPv6 addresses in no_proxy
  13. undici PR #5637: ignore trailing dots when matching no_proxy
  14. undici v8.11.0 docs: ProxyAgent
  15. undici v8.11.0 docs: EnvHttpProxyAgent
  16. libcurl: CURLOPT_NOPROXY
  17. curl changelog
  18. curl#19828: IPv6 CIDR notation in NO_PROXY (fixed in 8.18.0)
  19. Go: golang.org/x/net/http/httpproxy Config
  20. Go: httpproxy Config.ProxyFunc (localhost and loopback)
  21. golang.org/x/net commit a02ddfa7ea: httpproxy prefers lowercase proxy variables (x/net v0.58.0)
  22. golang/go#79656: inconsistent HTTP_PROXY precedence (milestone Go 1.28)
  23. Go 1.28 release-note draft: ProxyFromEnvironment prefers lowercase variables
  24. GNU Wget manual: Proxies
  25. Python 3.14 docs: urllib.request
  26. CPython 3.14.7 source: Lib/urllib/request.py (proxy_bypass by platform)
  27. Requests v2.34.0 release notes
  28. Requests v2.34.2 source: utils.py (should_bypass_proxies)
  29. Requests PR #7586: honor ports in IPv4 no_proxy entries
  30. httpx2 changelog (v2.13.1)
  31. httpx2 PR #1165: support IP CIDR ranges in NO_PROXY
  32. aiohttp docs: proxy support and trust_env
  33. aiohttp v3.14.3 source: helpers.py

Etiquetas:ProxiesTroubleshooting