Tools

no_proxy tester across curl, Go, Python and Java

One NO_PROXY string, five implementations. See which clients bypass the proxy for a given URL and which do not, with the matching rule that decided it.

Runs entirely in your browser · nothing is uploaded · updated 8 September 2026

Will this URL bypass the proxy?

What this tool does#

no_proxy has never been standardised. Every HTTP client implemented it independently, and they disagree about leading dots, wildcards, CIDR ranges, ports and even the separator character. The result is a service where curl reaches an internal host directly, the Go binary next to it goes through the proxy, and the Python job in the same container does something else again.

This tool evaluates one NO_PROXY value against one URL under five different matching engines at once, and tells you which entry matched and why. When the engines disagree, it says so, because that disagreement is usually the bug you are looking for.

The differences that actually bite#

Behaviourcurl 7.86.0+Go net/httpPython requests 2.34.0+CPython urllibJava nonProxyHosts
Separatorcommacommacommacommavertical bar
Reads NO_PROXY envyesyesyesyesno, uses a system property
.example.com matches example.comyes, the dot is skippedno, subdomains onlyyes, the dot is stripped (no before 2.34.0)yes, dots are strippedno, a dotted entry matches nothing at all
example.com matches sub.example.comyesyesyesyesno, needs *.example.com
Match is a plain string suffixno, label boundary requirednono since 2.34.0, label boundary required; yes before thatnono
Lone * disables the proxyyes, when the whole value is *yesnot by its own matcher, but on POSIX it falls through to urllib, which honours ityes, when the whole value is *yes, * is a wildcard so it matches every host
Glob patterns such as *.example.comnoyes, normalised to .example.com, so subdomains onlynonoyes, leading or trailing *
CIDR entriesyes, since 7.86.0yesIPv4 onlynono
localhost implicitly excludednoyesnonoyes, by default
Port qualified entrieshost only, ports are never comparedyes, host:portyes, host:port is tested alongside the bare hostyes, host compared with and without the portno

The row worth staring at is the plain string suffix in requests. Up to and including 2.33.x the comparison was a bare hostname.endswith(entry), so NO_PROXY=example.com also bypassed the proxy for notexample.com and evilexample.com. Requests 2.34.0 adopted CPython's fix and now prepends a dot before the suffix test, which closes the hole. It is still worth checking, because the version pinned in a lock file or a base image decides which behaviour you get, and if your egress policy depends on everything going through the proxy then an old pin is a policy hole rather than a convenience.

The second row worth staring at is the lone asterisk. NO_PROXY=* disables proxying in curl, Go, urllib and Java. In requests its own matcher never matches *, because "anything".endswith(".*") is false, but on POSIX the call falls through to urllib.request.proxy_bypass, which special-cases a value of exactly *, so the net effect there is still a full bypass. Treat a lone asterisk as a diagnostic switch rather than a configuration you ship.

The portable subset#

If the same value has to work for every client in a stack, restrict yourself to what all of them agree on:

Anything outside that subset needs testing per client. That is what the tool above is for.

Frequently asked questions#

Is no_proxy case sensitive?#

The variable name effectively is: most clients check NO_PROXY and no_proxy, and where both exist the precedence differs. The values are matched case insensitively against host names in curl, Go, CPython urllib and Java. requests is the exception: it lowercases the host from the URL but not the entries in your list, so an entry written as Internal.Example.COM never matches. Set both spellings to the same value, in lower case, and the question goes away.

Does no_proxy work with CIDR notation?#

In curl 7.86.0 and later, and in Go, yes, for both IPv4 and IPv6. In Python requests it works for IPv4 only, in CPython's urllib it does not work at all, and Java has no CIDR support either. Since the entry is matched against the host in the URL rather than a resolved address, a CIDR entry only helps when you connect to literal IP addresses.

Why does no_proxy not work for my hostname?#

The three usual causes, in order: the entry is a suffix of a different host than you think, the client resolves a name that never matches the entry because you wrote an IP and the URL uses a name, or the port in the URL makes the entry stop matching. Run the exact URL through the tool above and read the matching rule it reports.

Does NO_PROXY affect DNS?#

No. It only decides whether the client dials the proxy or the origin. If the origin is only resolvable through internal DNS, bypassing the proxy also means you need working internal resolution, which is a separate failure mode with a very similar symptom.

Which value wins if both NO_PROXY and no_proxy are set?#

It depends on the client and on its version, and that is the whole problem. Go has checked the uppercase form first in every released toolchain to date, though golang.org/x/net v0.58.0 switched to preferring lowercase. curl, requests and urllib read the lowercase form first. Set them identically and never rely on the precedence.

Primary sources#

Every normative claim on this page is checked against the specification or the vendor documentation listed here. Where behaviour is version dependent, the version is named in the text.

  1. curl documentation, NO_PROXY and --noproxy
  2. golang.org/x/net/http/httpproxy package documentation
  3. CPython urllib.request source, proxy_bypass_environment
  4. Requests source, requests/utils.py should_bypass_proxies
  5. Java networking properties, http.nonProxyHosts

More in proxy tools#