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:
# 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 comoexample.test,.example.test) fue la única escritura que cubrió un dominio y todos sus subdominios en los 15 clientes. Nunca coincidió connotexample.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.1evitó el proxy para127.0.0.1ylocalhosten todos los clientes. Sin entradas de loopback, solo Go se saltó el proxy para loopback.- wget solo lee
no_proxy. Elnet/httpde Go 1.27.1 prefiereNO_PROXYcuando ambas están definidas, y está previsto que Go 1.28 prefierano_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/httpde Go 1.27.1 conhttp.ProxyFromEnvironment;urllibde Python 3.14.7, requests 2.34.2, httpx 0.28.1, httpx2 2.13.1 y aiohttp 3.14.3 contrust_env=True;fetch()yhttp.get()integrados de Node.js 26.10.0 y 24.21.0, cada uno conNODE_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 condispatcher: new EnvHttpProxyAgent(), ambos en Node 26.10.0.
Estas entradas dieron el mismo resultado en los 15 clientes para las peticiones indicadas:
| Entrada | Resultado en todos los clientes |
|---|---|
example.test | Evitó el proxy para example.test; notexample.test siguió usando el proxy (los subdominios difirieron en el cliente http de Node) |
.example.test | Evitó el proxy para sub.example.test y a.b.example.test (el apex difirió en 4 clientes) |
example.test,.example.test | Evitó el proxy para el apex y ambos subdominios; notexample.test siguió usando el proxy |
192.0.2.10 | Evitó el proxy para esa dirección; 192.0.2.11 siguió usando el proxy |
EXAMPLE.TEST, o una petición a EXAMPLE.TEST | Las mayúsculas y minúsculas no cambiaron nada |
other.test , example.test | Un 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,::1 | Evitó el proxy para 127.0.0.1 y localhost ([::1] difirió; ver la sección sobre loopback) |
no_proxy definida, NO_PROXY sin definir | Todos 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ón | Evitaron el proxy | Usaron el proxy |
|---|---|---|
example.test | Node 26 fetch, Node 24 fetch, undici 7.29.1 | Los otros 12 |
sub.example.test, a.b.example.test | Go, fetch y http de Node 26 y 24, undici 7.29.1 y 8.11.0 | curl (ambos), wget, urllib, requests, httpx, httpx2, aiohttp |
notexample.test | Ninguno | Los 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ón | Evitaron el proxy | Usaron el proxy |
|---|---|---|
example.test, petición a sub.example.test | 13 clientes | Node 26 http, Node 24 http |
.example.test, petición a example.test | 11 clientes | wget, 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
| Valor | Evitaron el proxy | Usaron el proxy |
|---|---|---|
* | 14 clientes | wget |
other.test,* o " * " | Go, httpx, httpx2, Node 26 http, Node 24 http, undici 8.11.0 | curl (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.100en lugar de CIDR. El laboratorio no probó rangos con guion. Un comentario en node#57872 informa de que un rango con guion funcionó ennode:httppero no enfetch()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 para192.0.2.10en los 15 clientes y para192.0.2.11solo 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/24no 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]/:
| Entrada | Evitaron el proxy | No lo evitaron |
|---|---|---|
2001:db8::10 | 12 clientes | urllib, 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.0 | 8 usaron el proxy; httpx y httpx2 lanzaron un error |
2001:db8::/48 | curl 8.22.0, Go | 12 usaron el proxy; httpx lanzó un error |
[2001:db8::10]:8080, petición al puerto 8080 | Go, urllib, Node 26 fetch, Node 24 fetch, undici 7.29.1, undici 8.11.0 | 7 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:
| Entrada | Evitaron el proxy | Usaron el proxy |
|---|---|---|
example.test:8080 | 11 clientes | curl (ambos), wget, aiohttp |
192.0.2.10:8080 | 10 clientes | curl (ambos), wget, requests, aiohttp |
.example.test:8080 | 9 clientes | curl (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.testal 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 paraother.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 paraexample.testen 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 ahttp://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:
| Entorno | Evitaron el proxy | Usaron el proxy |
|---|---|---|
Solo NO_PROXY=example.test | 14 clientes | wget |
Solo no_proxy=example.test | Los 15 | Ninguno |
NO_PROXY=example.test, no_proxy=other.test | Go 1.27.1 | Los otros 14 |
NO_PROXY=example.test, no_proxy="" | curl (ambos), Go, requests, Node 26 http, Node 24 http | wget, 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.
| Valor | 127.0.0.1 y localhost | [::1] |
|---|---|---|
localhost,127.0.0.1,::1 | Evitado 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="".
| Diferencia | fetch | http | undici 8.11.0 |
|---|---|---|---|
| Dominio sin punto, petición a un subdominio | Evitado | Proxy | Evitado |
*.example.test, petición al apex | Evitado | Proxy | Proxy |
* dentro de una lista | Proxy | Evitado | Evitado |
| Dirección IPv6 entre corchetes | Evitado | Proxy | Evitado |
| Punto inicial con un puerto | Evitado | Proxy | Evitado |
| Entrada con punto final | Evitado | Proxy | Evitado |
| Espacios en lugar de comas | Evitado | Proxy | Evitado |
no_proxy vacío, NO_PROXY definido | Proxy | Evitado | Proxy |
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:
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 maincompare 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:
- no-proxy-matching-tested.zip: todos los archivos de abajo, con
clients/en su sitio (recréalo si descargas los archivos uno a uno) - README.md: método, requisitos, comandos y cómo leer los resultados
- results.json y results.csv: todas las celdas de la ejecución publicada
- no_proxy_lab.py, clients/py_client.py, clients/node_client.cjs, clients/go_client.go y nonet.sb: el harness, los clientes de una sola petición y el perfil de sandbox solo loopback
- requirements.txt, package.json y package-lock.json: las versiones exactas fijadas de Python y npm
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
- GitLab (2021): We need to talk: Can we standardize NO_PROXY?
- nodejs/node#57872: node:http vs fetch() NO_PROXY table (comment, 2026-09-01)
- nodejs/node#65616: NO_PROXY=example.com and http.request() subdomains
- nodejs/node#65617: http: match subdomains for plain NO_PROXY entries
- nodejs/node#66202: fetch() and http.request() with an empty lowercase variable
- Node.js 26.10.0 docs: NO_PROXY format
- Node.js 26.10.0 docs: NODE_USE_ENV_PROXY
- Node.js 26.10.0 source: lib/internal/http.js (http.request matcher)
- Node.js 26.10.0: bundled undici version
- undici PR #5777: NO_PROXY wildcard semantics
- undici v8.11.0 release notes
- undici PR #5623: match bare IPv6 addresses in no_proxy
- undici PR #5637: ignore trailing dots when matching no_proxy
- undici v8.11.0 docs: ProxyAgent
- undici v8.11.0 docs: EnvHttpProxyAgent
- libcurl: CURLOPT_NOPROXY
- curl changelog
- curl#19828: IPv6 CIDR notation in NO_PROXY (fixed in 8.18.0)
- Go: golang.org/x/net/http/httpproxy Config
- Go: httpproxy Config.ProxyFunc (localhost and loopback)
- golang.org/x/net commit a02ddfa7ea: httpproxy prefers lowercase proxy variables (x/net v0.58.0)
- golang/go#79656: inconsistent HTTP_PROXY precedence (milestone Go 1.28)
- Go 1.28 release-note draft: ProxyFromEnvironment prefers lowercase variables
- GNU Wget manual: Proxies
- Python 3.14 docs: urllib.request
- CPython 3.14.7 source: Lib/urllib/request.py (proxy_bypass by platform)
- Requests v2.34.0 release notes
- Requests v2.34.2 source: utils.py (should_bypass_proxies)
- Requests PR #7586: honor ports in IPv4 no_proxy entries
- httpx2 changelog (v2.13.1)
- httpx2 PR #1165: support IP CIDR ranges in NO_PROXY
- aiohttp docs: proxy support and trust_env
- aiohttp v3.14.3 source: helpers.py