Skip to content
Web scraping

Proxies for git, npm and pip

git config https.proxy is silently ignored, npm's equivalent works, and pip blames the package. Every setting tested against a closed port.

Three developer tools, three different proxy settings, and one of them is a trap: the option that looks correct in git does nothing at all, and it fails silently. This guide gives the working configuration for each, the errors they produce when it goes wrong, and how to prove the proxy is actually being used.

Everything below was produced by pointing each setting at a port that refuses connections. If the command fails, the setting was honoured. If it succeeds, the tool ignored the setting and went direct. Each tool has a passing no-proxy control, so a failure means the proxy rather than a broken test.

#The short answer

Setting git 2.50.1 pip 25.0 npm 11.6.3
https_proxy / HTTPS_PROXY Honoured Honoured Honoured
http_proxy / HTTP_PROXY Ignored Ignored Ignored
ALL_PROXY / all_proxy Honoured Not tested Not tested
The tool’s own option http.proxy --proxy --proxy
An https variant of that option Ignored None exists --https-proxy works
no_proxy bypass Honoured Honoured --noproxy

Two rows are worth pausing on. Every one of the three ignores http_proxy when the endpoint is HTTPS, which is correct behaviour and still surprises people who set only that one. And the fifth row is where git and npm disagree completely.

#git: http.proxy is the key, even for HTTPS

This is the mistake that costs the most time, because the wrong configuration reports success.

# Works. Applies to https:// remotes as well.
git config --global http.proxy http://gateway.example:8000

# Does nothing at all. No error, no warning, traffic goes direct.
git config --global https.proxy http://gateway.example:8000

Tested by pointing each at a closed port. With http.proxy the clone failed, which proves git tried to use it. With https.proxy the clone succeeded — git ignored the setting entirely and connected straight to the remote.

The reason is that git has no such key. Its proxy configuration is http.proxy regardless of the URL scheme, plus the URL-scoped form http.<url>.proxy for per-host rules. Writing https.proxy creates a config entry that nothing ever reads, and git config --list will happily show it back to you.

Per-host configuration uses the same key with a URL in the middle:

# Only this host goes through the proxy
git config --global http.https://github.com/.proxy http://gateway.example:8000

# Everything except this host
git config --global http.proxy http://gateway.example:8000
git config --global http.https://internal.example/.proxy ""

The empty string in the second form is the documented way to say “no proxy for this one”, and it is more reliable than no_proxy because it lives with the rest of your git configuration rather than in the shell that happened to start the command.

#npm: the opposite convention

npm accepts both spellings, and both work:

npm config set proxy       http://gateway.example:8000
npm config set https-proxy http://gateway.example:8000

Both were confirmed against a closed port, and both failed as expected. So the option that is a no-op in git is functional in npm. If you carry a mental model from one tool to the other, this is where it breaks.

npm spells its exemption list differently too. It is noproxy, one word, not no_proxy:

npm config set noproxy registry.internal.example,localhost

One caution when testing npm: it answers from its cache. A command that succeeds through a deliberately broken proxy may simply have avoided the network. Point --cache at a fresh temporary directory when you are checking proxy behaviour, or you will measure the cache instead.

#pip

pip is the most conventional of the three. It reads the environment, and it has one flag:

# Per command
pip install --proxy http://gateway.example:8000 requests

# Persistent, in pip.conf
[global]
proxy = http://gateway.example:8000

Both the environment variables and --proxy were confirmed working. no_proxy is honoured, and it needs both hosts when you exempt PyPI, because packages are downloaded from a different domain than the index:

export no_proxy="pypi.org,files.pythonhosted.org"

Exempting only the first leaves every actual download still going through the proxy, which is the kind of half-configuration that produces an intermittent failure rather than an obvious one.

#Reading the errors

The three tools differ enormously in how clearly they report a proxy failure. Each line below was produced deliberately, with the proxy pointed at a closed port or an unresolvable host.

Tool What it prints Does it name the proxy?
git, port closed fatal: unable to access ... Failed to connect to 127.0.0.1 port 9 Shows the address
git, host unresolvable fatal: ... Could not resolve proxy: no-such-proxy.invalid Yes, explicitly
npm npm error code ECONNREFUSED / syscall connect Implies it
pip ERROR: Could not find a version that satisfies the requirement six No. It blames the package.

The pip row is the one to remember. A broken proxy produces a message that reads exactly like a package that does not exist, followed by No matching distribution found. Nothing in the output mentions a proxy, a connection or a network. People spend real time checking spelling and package availability before suspecting the thing that is actually wrong.

The rule that saves the time: if a package manager says a package cannot be found, and you are behind a proxy, test the proxy before you test the package name.

#Proving the proxy is applied

Do not infer this from the absence of errors. A tool that ignores your setting succeeds quietly, which looks identical to a tool that used the proxy correctly.

The reliable test is to point the setting at a port where nothing is listening. A configuration that is being honoured must fail:

# If these succeed, the setting is being ignored
https_proxy=http://127.0.0.1:9 git ls-remote https://github.com/octocat/Hello-World.git
https_proxy=http://127.0.0.1:9 pip download six --no-deps -d /tmp/x
https_proxy=http://127.0.0.1:9 npm view express version

Run the same three commands with the variable unset first. If they fail then too, you have a network problem rather than a proxy result, and every conclusion you draw from the test would be wrong.

For a stronger check, run a proxy that logs what it is asked for and confirm the connection appears. A tool that never contacts the proxy leaves nothing in that log, whatever its exit code says.

#Where each setting is stored

Once you stop passing the proxy per command, it has to live in a file, and each tool keeps its own. Knowing which file also tells you where a stale setting will be hiding when the proxy changes.

Tool Per-user file How to find it
git ~/.gitconfig, or $XDG_CONFIG_HOME/git/config git config --global --list --show-origin
npm ~/.npmrc npm config get userconfig
pip ~/.pip/pip.conf or ~/.config/pip/pip.conf pip config debug

Each also has a system-wide layer above the per-user one — /etc/gitconfig, an npmrc beside the npm installation, and a global pip.conf. On a machine somebody else set up, a proxy you cannot find in your own files is usually sitting in one of those.

The commands in the third column are worth running before you debug anything else. --show-origin and pip config debug both report which file a value came from, which turns “why is it still using the old proxy” into a one-line answer.

#Keep the password out of the file

A proxy URL with inline credentials leaks in more places than people expect. In these three tools specifically it ends up in git config --list output, in ~/.npmrc as plain text, and in any CI log that echoes the environment.

# Avoid: the password is now in a file, in shell history, and in --list output
git config --global http.proxy http://user:[email protected]:8000

Where the provider supports it, prefer an address allow-list so no secret travels with the request at all. Where credentials are unavoidable, keep them in an environment variable supplied at run time rather than written into a config file, and make sure whatever prints your configuration for debugging redacts it.

#Where none of this reaches

All three tools read the environment of the process that runs them. That means the settings apply only where they were inherited, and three boundaries commonly break them.

  • A container does not inherit your shell. Variables exported in your terminal are not passed to docker run unless you name them, and a build runs each step in its own container again. Pulling the image is a third case, made by the daemon rather than your shell.
  • A service manager does not inherit your login shell. A unit started by systemd, or a job started by cron, gets the environment it is given. Exporting a variable in a shell profile and restarting the service changes nothing.
  • CI runners start clean. The proxy that works on your laptop is absent in the pipeline unless the pipeline sets it, which is why “works locally, fails in CI” is the standard symptom.

For the general behaviour of these variables across runtimes, including the case rule that makes uppercase HTTP_PROXY unreliable, see the environment-variable guide. The authentication notes there apply here too: a proxy URL containing a password ends up in shell history, in git config --list output and in CI logs.

#How this was tested

Each setting was pointed at http://127.0.0.1:9, a port that refuses connections, so that a successful command proves the setting was skipped and a failed one proves it was attempted. Every tool was given a control run with no proxy set; where that control failed, the results were discarded rather than reported.

That control mattered. Two earlier versions of this test produced a clean-looking table in which every setting appeared to be honoured, and both were wrong: in one the test harness was failing before the tool ran, and in the other the shell was not splitting the command as intended. In both cases the failure looked exactly like a proxy being used. The tables above come from the run where all three controls passed.

Versions: git 2.50.1, pip 25.0, npm 11.6.3, on macOS, on 1 September 2026. Behaviour changes between releases, so run the closed-port check against your own versions rather than trusting a table.

Docker is deliberately absent. We could not run a daemon on the test machine, and we do not publish configuration we have not executed.

Frequently asked questions

Why does git config https.proxy not work?
Because git has no such key. Its proxy setting is http.proxy regardless of the URL scheme, plus the URL-scoped form http.<url>.proxy for per-host rules. Writing https.proxy creates an entry nothing reads, and git config --list will still show it back to you, which is why the mistake survives so long.
Which environment variable should I set for git, npm and pip?
https_proxy, in lowercase, for all three. Every one of them ignores http_proxy when the endpoint is HTTPS, and package registries and git remotes are almost always HTTPS today. Setting both cases costs nothing and removes the question.
pip says it cannot find the package. Is that really a proxy problem?
It can be. A broken proxy makes pip print "Could not find a version that satisfies the requirement" followed by "No matching distribution found", with no mention of a proxy or a network. If you are behind a proxy, test the proxy before you check the package name.
How do I exempt an internal host from the proxy?
For git, set an empty URL-scoped value: git config http.https://internal.example/.proxy "". That lives with your git configuration rather than in the shell. For pip, use no_proxy and remember to list files.pythonhosted.org as well as pypi.org, because downloads come from a different domain than the index. For npm, the setting is spelled noproxy, one word.
Why did my npm proxy test pass when the proxy was broken?
npm answered from its cache without touching the network. Point --cache at a fresh temporary directory when you are testing proxy behaviour, otherwise you are measuring the cache rather than the connection.
How can I be sure the proxy is actually being used?
Point it at a port where nothing is listening. A setting that is being honoured must make the command fail. Run the same command with the variable unset first; if that fails too, you have a network problem and the test proves nothing.

Sources

  1. git config: the http.proxy and http.<url>.proxy settings
  2. pip user guide: proxy configuration and config files
  3. npm config: proxy, https-proxy and noproxy

Read this page as Markdown · Quote it freely under CC BY 4.0 with a link back.