Git Config Proxy Setup for HTTPS and SSH Remotes
Git config proxy setup that works: http.proxy globally or per remote, URL-encoded credentials, no_proxy, unsetting it, and ProxyCommand for SSH remotes.
The git config proxy setting you need depends on one thing most guides skip: the scheme of your remote URL. git config --global http.proxy covers https:// remotes and does nothing at all for git@host:org/repo.git remotes, which go through OpenSSH and need a ProxyCommand. This guide starts from the timeout you're staring at, tells you which path your remote takes, and then gives the exact commands for each, including credential encoding, per-host scoping, and how to turn the proxy back off. All but one of the error strings below were reproduced on our own test setup (described at the end of the first section), and the exception is marked.
The Symptom: git clone and git push Hang, Then Time Out
On a network where outbound traffic must go through a proxy, git doesn't fail fast. It tries a direct connection, the firewall drops the packets, and you wait. Depending on the remote, the failure you finally see looks like one of these:
fatal: unable to access 'https://git.sparkproxy.io/team/app.git/': Failed to connect to git.sparkproxy.io port 443 after 21030 ms: Could not connect to server
ssh: connect to host git.sparkproxy.io port 22: Connection timed out
fatal: Could not read from remote repository.
The first line comes from libcurl, which git uses for HTTP and HTTPS remotes. The second comes from OpenSSH. They are two different programs with two different proxy settings, and that split is the root of most "I set the proxy and it still doesn't work" threads.
How we tested: Git 2.51.0.windows.1 (built against libcurl 8.15.0) and OpenSSH 10.0p2 on Windows Server 2022, talking to a public GitHub repository through a small logging proxy we ran on 127.0.0.1. That proxy spoke HTTP CONNECT with Basic auth on one port and SOCKS5 with username and password on another, and wrote every request and every decoded credential to a log, so we could see exactly what git sent. We also checked the SparkProxy gateway's real response to an unauthenticated request. No speed or success-rate figures appear in this post because we didn't measure any.
Read Your Remote URL Before Touching Config
Run this inside the repository, or look at the URL you're passing to git clone:
git remote -v
# origin https://git.sparkproxy.io/team/app.git (fetch)
# origin git@git.sparkproxy.io:team/app.git (push)
Yes, fetch and push can differ. A pushurl set to SSH while fetch uses HTTPS is common, and it explains the "clone works, push hangs" report.
| Remote URL looks like | Transport | Proxy setting that applies | Covered in |
|---|---|---|---|
| `https://host/org/repo.git` | libcurl | `http.proxy`, `http. | Sections 3 to 7 |
| `http://host/org/repo.git` | libcurl | Same keys, but the env var is `http_proxy` | Sections 3 to 7 |
| `git@host:org/repo.git` | OpenSSH | `ProxyCommand` in `~/.ssh/config` or `core.sshCommand` | Sections 8 and 9 |
| `ssh://git@host:2222/org/repo.git` | OpenSSH | Same as above, with port 2222 | Sections 8 and 9 |
| `git://host/org/repo.git` | Raw TCP, port 9418 | `core.gitProxy` or `GIT_PROXY_COMMAND` | Brief note in section 8 |
We confirmed the SSH row directly. With http.proxy pointed at our logging proxy, git ls-remote git@github.com:octocat/Hello-World.git connected straight to GitHub and the proxy log stayed empty. Git never consults http.proxy for an SSH remote.
If the proxy is all you're allowed and you don't strictly need SSH, the fastest fix is often to switch the remote to HTTPS:
git remote set-url origin https://git.sparkproxy.io/team/app.git
Scraping at scale? Skip the blocks.
Fast, unblockable datacentre proxies with unlimited bandwidth.
Set http.proxy Globally
For HTTPS remotes, one line covers every repository for your user account:
git config --global http.proxy http://spark_user:s3cret@gateway.sparkproxy.io:11000
git ls-remote https://git.sparkproxy.io/team/app.git HEAD
ls-remote ... HEAD is the cheapest end-to-end test: one request to the server, no objects downloaded. If it prints a commit hash, the proxy path works.
The git-config reference gives the value syntax as [protocol://][user[:password]@]proxyhost[:port][/path]. In practice:
| Value you write | What git does |
|---|---|
| `http://host:port` | Plain HTTP proxy, CONNECT for HTTPS targets (the normal case) |
| `host:port` (no scheme) | Treated as `http://`; we confirmed this works |
| `https://host:port` | TLS to the proxy itself; only for proxies that terminate TLS |
| `socks5://host:port` | SOCKS5, git resolves the target hostname locally |
| `socks5h://host:port` | SOCKS5, the proxy resolves the hostname |
| `socks4://`, `socks4a://` | SOCKS4 variants, rarely useful today |
Two of those rows bit us during testing. Pointing an https:// proxy URL at a plain HTTP proxy didn't error; the command hung until we killed it at 40 seconds, because libcurl sat waiting for a TLS handshake the proxy never started. And a typo'd scheme (htp://) was silently accepted by Git 2.51 and treated as plain HTTP. The current Git source (http.c on the master branch, read 2026-10-30) adds an "unsupported proxy scheme" error for that case, so newer releases may reject it.
The difference between socks5 and socks5h showed up clearly in the proxy log: with socks5h the SOCKS request carried the hostname github.com, while with socks5 it carried an IPv4 address that our machine had already resolved. On a network that blocks outside DNS, only socks5h works. The SOCKS5 setup guide explains the same h suffix for Python, Node.js and browsers.
If you only need the proxy for a single command, skip the config file entirely:
git -c http.proxy=http://gateway.sparkproxy.io:11000 clone https://git.sparkproxy.io/team/app.git
Use --system instead of --global to set it machine-wide, or no flag at all to set it in the current repository's .git/config.
Scope the Proxy to One Host or One Remote
A global proxy is wrong when some remotes are internal and some are outside. Git has two scoping mechanisms, and they solve slightly different problems.
URL-scoped config matches the remote URL, so it applies across every repository that talks to that host:
# Proxy only for the external forge
git config --global http.https://git.sparkproxy.io/.proxy http://gateway.sparkproxy.io:11000
# Check what git would use for a given URL
git config --get-urlmatch http.proxy https://git.sparkproxy.io/team/app.git
The matching rules in the git-config docs are worth knowing. Scheme and port must match exactly. The host may use one per label, so https://.sparkproxy.io/ matches git.sparkproxy.io but not a.b.sparkproxy.io. The path matches on slash boundaries, and a longer path wins. We verified the negative case too: with the proxy scoped to https://git.sparkproxy.io/, a clone from a different host went direct and left nothing in the proxy log.
Remote-scoped config attaches the proxy to a named remote in one repository:
git config remote.origin.proxy http://gateway.sparkproxy.io:11000
git config remote.upstream.proxy "" # empty string = no proxy for this remote
The docs limit remote. to "remotes that require curl (http, https and ftp)", so like everything else in this section it's ignored for SSH URLs. The resulting .gitconfig looks like this, which is handy if you manage dotfiles:
[http]
proxyAuthMethod = basic
[http "https://git.sparkproxy.io/"]
proxy = http://spark_user@gateway.sparkproxy.io:11000
[http "https://git.internal.sparkproxy.io/"]
proxy =
Proxy Credentials and URL-Encoding
The proxy value is a URL, so any reserved character in the username or password must be percent-encoded. Our test proxy used the password p@ss:w0rd on purpose. Here is what reached it:
| Value in `http.proxy` | Credentials the proxy decoded | Result |
|---|---|---|
| `http://spark_user:p%40ss%3Aw0rd@127.0.0.1:28480` | user `spark_user`, pass `p@ss:w0rd` | Clone succeeded |
| `http://spark_user:p@ss:w0rd@127.0.0.1:28480` | user `ss`, pass `w0rd` | `CONNECT tunnel failed, response 407` |
| `http://spark_user:wrong@127.0.0.1:28480` | user `spark_user`, pass `wrong` | `CONNECT tunnel failed, response 407` |
The middle row is the trap. URL parsing splits at the last @, so everything before it became garbage and the proxy saw a username of ss. The error is identical to a wrong password, which sends people off resetting credentials that were fine.
Common encodings: @ is %40, : is %3A, / is %2F, # is %23, % is %25, a space is %20. Rather than encode by hand, generate the value:
"""Build a percent-encoded proxy URL for git's http.proxy setting."""
import sys
from urllib.parse import quote
def proxy_url(user: str, password: str, host: str, port: int, scheme: str = "http") -> str:
# safe="" forces '/', '@' and ':' to be encoded as well
return f"{scheme}://{quote(user, safe='')}:{quote(password, safe='')}@{host}:{port}"
if __name__ == "__main__":
print(proxy_url(sys.argv[1], sys.argv[2], "gateway.sparkproxy.io", 11000))
python encode_proxy.py spark_user 'p@ss:w0rd'
# http://spark_user:p%40ss%3Aw0rd@gateway.sparkproxy.io:11000
Keep the password out of the config file. The git-config docs allow a proxy URL with a username and no password, in which case git asks for one "in the same way it does for other credentials", meaning your credential helper. We tested it with http.proxy=http://spark_user@127.0.0.1:28480 and a GIT_ASKPASS script; git called the script with the prompt Password for 'http://spark_user@127.0.0.1:28480': , used the answer, and the clone went through. With a helper such as Git Credential Manager or credential.helper=store, you're asked once.
Save a round trip. The default http.proxyAuthMethod is anyauth, which, per the docs, assumes the proxy answers an unauthenticated request with a 407. Our proxy log showed exactly that: every connection began with a CONNECT carrying no credentials, then a second one with them. The SparkProxy gateway answers that first probe with 407 Proxy Authentication Required and Proxy-Authenticate: Basic realm="SparkProxy", so anyauth works against it, but setting basic skips the probe:
git config --global http.proxyAuthMethod basic
For how Basic, Digest and NTLM proxy auth differ on the wire, see how proxy authentication works.
Which Setting Wins: Config, Environment or no_proxy
Git reads its own config first and falls back to environment variables only when no http.proxy applies. We ran each case against the logging proxy:
| Setup | What happened |
|---|---|
| `https_proxy` set, no config | Proxied |
| Only `http_proxy` set, remote is `https://` | **Went direct**, proxy log empty |
| `https_proxy` points at a dead port, `http.proxy` is valid | Proxied via `http.proxy`; env ignored |
| `http.proxy` set, `no_proxy=github.com` | **Went direct** |
| `http.proxy` set to empty string, `https_proxy` set | Went direct; empty value disables proxying |
The second row catches a lot of people. Git's source chooses HTTPS_PROXY or https_proxy for an HTTPS remote and http_proxy only for an HTTP one, then falls back to ALL_PROXY or all_proxy. Exporting just http_proxy and then cloning an https:// URL sends the request straight at the firewall, which looks exactly like the original timeout.
The fourth row surprises people from the other direction. Even with an explicit http.proxy, git passes NO_PROXY or no_proxy from the environment to libcurl, and a match bypasses the proxy. If a clone ignores your carefully set proxy, run echo $no_proxy before anything else; a stale entry left by a VPN client or a container image is a common cause.
To find where a value comes from when several files are involved:
git config --show-origin --show-scope --get-regexp 'proxy'
env | grep -i _proxy
Unset the Proxy or Exempt Internal Hosts
Taking a laptop off the corporate network is the usual reason to remove the setting:
git config --global --unset http.proxy
git config --global --unset http.proxyAuthMethod
git config --global --remove-section http.https://git.sparkproxy.io/
# Git 2.46 and later also accept the subcommand form
git config unset --global http.proxy
If --unset exits with status 5, the key wasn't in that file. The git-config docs list 5 as the code for trying to "unset an option which does not exist", so check the other scopes with the --show-origin command above; a value in the repository's .git/config or the system file still applies.
To keep a global proxy but exempt internal hosts, you have two choices. A URL-scoped empty value stays inside git and travels with your dotfiles:
git config --global http.https://git.internal.sparkproxy.io/.proxy ""
Or use no_proxy, which also affects curl, pip and everything else that reads it:
export no_proxy="localhost,127.0.0.1,.internal.sparkproxy.io"
Don't forget the environment variables. Unsetting http.proxy while https_proxy is still exported just moves git onto the env-var proxy.
SSH Remotes: ProxyCommand Through CONNECT or SOCKS5
For git@host: and ssh:// URLs, the proxy belongs to OpenSSH. Its ProxyCommand option, per the ssh_config manual, runs a command that "should read from its standard input and write to its standard output", with %h and %p replaced by the target host and port. The helper opens a tunnel through the proxy and ssh talks through it. With an HTTP proxy that tunnel is a CONNECT request; the HTTP tunnel explainer covers what CONNECT does on the wire, so we'll stick to configuration here.
Put it in ~/.ssh/config so every git command picks it up:
Host git.sparkproxy.io
User git
ProxyCommand connect -H gateway.sparkproxy.io:11000 %h %p
Which helper you use matters more than the guides admit, because they differ on authentication:
| Helper | Where you get it | HTTP CONNECT with user:pass | SOCKS5 with user:pass |
|---|---|---|---|
| `connect` 1.105 | Ships with Git for Windows (`mingw64\bin\connect.exe`); Ubuntu package `connect-proxy` | Yes, via `HTTP_PROXY_USER` / `HTTP_PROXY_PASSWORD` | Yes, via `SOCKS5_USER` / `SOCKS5_PASSWD` |
| `nc` (OpenBSD netcat, Ubuntu 24.04 `netcat-openbsd` 1.226) | Preinstalled on most Linux distros and macOS | `-P user`, then prompts for the password | No; its man page says proxy auth "is only supported for HTTP CONNECT proxies at present" |
| `ncat` | Nmap project | Yes, `--proxy-auth user:pass` | Yes, same flag with `--proxy-type socks5` |
The nc row is the one that breaks CI. Its password prompt needs a terminal, so a build agent sits there forever, and SOCKS5 with credentials isn't possible at all. On a pipeline, use connect or ncat, or switch the proxy to IP-whitelist authentication so no credentials are needed.
We ran both connect modes against GitHub through the logging proxy, with no SSH key loaded on purpose. Reaching GitHub's own Permission denied (publickey) message proves the tunnel worked:
# HTTP CONNECT
export HTTP_PROXY_USER=spark_user HTTP_PROXY_PASSWORD='s3cret'
ssh -o ProxyCommand="connect -H gateway.sparkproxy.io:11000 %h %p" -T git@git.sparkproxy.io
# SOCKS5; connect passes the hostname, so DNS resolves at the proxy
export SOCKS5_USER=spark_user SOCKS5_PASSWD='s3cret'
ssh -o ProxyCommand="connect -S spark_user@gateway.sparkproxy.io:13000 %h %p" -T git@git.sparkproxy.io
The proxy log showed CONNECT github.com:22 for the first and a SOCKS5 request carrying the hostname github.com for the second. Note that credentials here go in environment variables, not in the URL, so the percent-encoding problem from section 5 doesn't arise.
The nc equivalents, for an IP-whitelisted proxy:
ProxyCommand nc -X connect -x gateway.sparkproxy.io:11000 %h %p # HTTP CONNECT
ProxyCommand nc -X 5 -x gateway.sparkproxy.io:13000 %h %p # SOCKS5
If you'd rather not touch ~/.ssh/config, scope the proxy to git alone with core.sshCommand, which the docs describe as used by git fetch and git push "instead of ssh":
git config --global core.sshCommand "ssh -o ProxyCommand='connect -H gateway.sparkproxy.io:11000 %h %p'"
# One-off, without saving anything
GIT_SSH_COMMAND="ssh -o ProxyCommand='connect -S gateway.sparkproxy.io:13000 %h %p'" git push
The GIT_SSH_COMMAND variable overrides core.sshCommand when both are set. For the rare git:// remote, core.gitProxy takes a command invoked as command host port, and the special value none exempts a domain.
When the Proxy Only Allows Port 443
Many corporate proxies refuse CONNECT to any port except 443. With a correct ProxyCommand you then get a fast refusal instead of a timeout. We set our test proxy to answer 403 for any CONNECT not aimed at 443, and connect printed FATAL: failed to begin relaying via HTTP. followed by ssh's own Connection closed by UNKNOWN port 65535. A wrong proxy password produces the same two lines, preceded by ERROR: Authentication failed., which is how you tell them apart.
Some git hosts run SSH on 443 for exactly this. GitHub's documentation says "the hostname for port 443 is ssh.github.com, not github.com", and gives this config:
Host github.com
Hostname ssh.github.com
Port 443
User git
ProxyCommand connect -H gateway.sparkproxy.io:11000 %h %p
The ProxyCommand line is our addition. We tested ssh -p 443 -T git@ssh.github.com through the logging proxy and it reached GitHub's publickey check, with the log showing CONNECT ssh.github.com:443. Expect a new host-key prompt the first time, because [ssh.github.com]:443 is a separate known_hosts entry; compare the fingerprint against the host's published list before accepting it. If your git server has no port-443 endpoint, HTTPS with a personal access token is the remaining route.
Diagnose a Git Proxy Failure From Its Error Text
Work down this list in order, cheapest check first. The strings are verbatim from Git 2.51 / libcurl 8.15 and OpenSSH 10.0p2; other builds may word them slightly differently.
| Error you see | Likely cause | Fix | Proxy config can fix it? |
|---|---|---|---|
| `Failed to connect to | No proxy applied; git went direct | Set `http.proxy`, or `https_proxy` rather than `http_proxy`; check `no_proxy` | Yes |
| `Failed to connect to 127.0.0.1 port 28499 after 2019 ms: Could not connect to server` (proxy host named) | Proxy host or port wrong, or proxy down | Fix host:port; test with `curl -x` | Yes |
| `CONNECT tunnel failed, response 407` | Missing, wrong or badly encoded credentials | Percent-encode; try `http.proxyAuthMethod`; check the account | Yes |
| Command hangs with no output | `https://` proxy scheme on a plain HTTP proxy | Use `http://` for the proxy URL | Yes |
| `ssh: connect to host | SSH remote, no ProxyCommand | Section 8, or switch the remote to HTTPS | Yes |
| `ERROR: Authentication failed.` then `FATAL: failed to begin relaying via HTTP.` | `connect` sent wrong proxy credentials | Fix `HTTP_PROXY_USER` / `HTTP_PROXY_PASSWORD` | Yes |
| `FATAL: failed to begin relaying via HTTP.` then `Connection closed by UNKNOWN port 65535` | Proxy refuses CONNECT to port 22 | Port 443 endpoint (section 9) or HTTPS | Partly |
| `SSL certificate problem: unable to get local issuer certificate` (standard libcurl text, not reproduced by us) | Proxy intercepts TLS with a private CA | `http.sslCAInfo` pointing at the CA bundle, or `http.sslBackend=schannel` on Windows | **No**, it's a trust issue |
| `git@ | Tunnel works; key not accepted | Load the right key, check the account | **No** |
| `remote: Repository not found.` | Tunnel works; URL, token or permissions wrong | Fix the URL or the credential for the git host | **No** |
The TLS row deserves a warning. The git-config docs state that any proxy "must be completely transparent", and a TLS-intercepting appliance isn't. Don't paper over it with http.sslVerify=false; get the company CA certificate and point http.sslCAInfo at it. On Windows, http.sslBackend=schannel makes git use the Windows certificate store, where IT usually deploys that CA already. If schannel then complains about checking revocation status, the docs say http.schannelCheckRevoke=false is the switch, and it should be the last thing you try.
The last two rows are good news in disguise: the network path is fine and only the git host's authentication is left.
Using SparkProxy as the Proxy for Git
If your employer runs the proxy, use theirs; nothing above changes except the hostname. Choosing your own egress makes sense for CI runners that need a predictable exit, for testing how a self-hosted forge behaves for users in other countries, or for build machines with no outbound route except through a proxy you control. The gateway, gateway.sparkproxy.io, takes HTTP/HTTPS and SOCKS5 with either IP-whitelist or user:pass authentication (all three ports below accepted TCP connections when we checked on 2026-10-30):
| Port | Mode | Git use |
|---|---|---|
| `11000` | Random Rotation, a new IP per connection | One-off clones and fetches |
| `11002` | Sticky Sessions, the same IP held for 5 minutes | A fetch followed by a push, or a server that pins tokens to IPs |
| `13000` | SOCKS5 | `socks5h://` in `http.proxy`, or `connect -S` for SSH |
A smart-HTTP clone is several separate HTTPS requests, so on port 11000 each one can leave from a different IP. Public forges generally don't care. If a server logs you out between requests, or an allowlist expects one address, use 11002. We haven't measured what happens to a long-running tunnel when a sticky session's 5 minutes run out, so test a large push before relying on it.
IP-whitelist auth is the tidy option for build agents. With the runner's address whitelisted, the git config holds no secret at all, and nc -X connect works without its interactive password prompt:
git config --global http.proxy http://gateway.sparkproxy.io:11002
git config --global core.sshCommand "ssh -o ProxyCommand='nc -X connect -x gateway.sparkproxy.io:11002 %h %p'"
We didn't have an account on the test machine, so we didn't verify whether the gateway allows CONNECT to port 22. Check with ssh -v and fall back to a port-443 SSH endpoint or HTTPS if it's refused.
On cost: the SparkProxy homepage, read 2026-10-30, lists plans "Starting from $75/month" (USD), with unlimited bandwidth on every plan and a 24-hour free trial with no credit card. Plans are priced by pack and concurrency, not per GB, so a multi-gigabyte monorepo clone doesn't change the bill. Term discounts are 5% for 3 months, 10% for 6 and 15% for 12. The pool is advertised as 1M+ IPs in 80+ countries; that's SparkProxy's own claim. For running other containerised tools through the same gateway, the Docker proxy guide covers build-time and runtime settings.
Frequently asked questions
FAQ
Run git config http.proxy http://host:port inside the repository without --global, and the value lands in that repo's .git/config. For one remote only, use git config remote.origin.proxy http://host:port.
Because SSH remotes (git@host: or ssh://) are handled by OpenSSH, not libcurl. Set a ProxyCommand in ~/.ssh/config or in core.sshCommand, using connect, nc or ncat as the helper.
Percent-encode it: @ becomes %40 and : becomes %3A. An unencoded @ makes git split the URL at the last @, and the proxy answers 407 as if the password were wrong. Leaving the password out entirely lets git's credential helper prompt for it.
Run git config --global --unset http.proxy, then check git config --show-origin --get-regexp proxy for copies in other scopes. Also clear https_proxy and all_proxy from your environment, or git will keep using them.
Yes. For HTTPS remotes, set http.proxy to socks5h://user:pass@host:port; with the h, name lookups happen at the proxy rather than on your machine. For SSH remotes, use ProxyCommand connect -S host:port %h %p, since OpenBSD nc can't authenticate to SOCKS5.
Usually the push URL is SSH while the fetch URL is HTTPS. Run git remote -v; if the push line starts with git@, either add a ProxyCommand or set the push URL to HTTPS with git remote set-url --push origin https://....
Get 20% off your first month
Premium datacentre proxies with unlimited bandwidth. Use the code at checkout.
Save up to 15% more on quarterly, half-yearly and yearly plans
Related articles
SSL WRONG_VERSION_NUMBER With a Proxy: The Fix
Getting SSL WRONG_VERSION_NUMBER through a proxy in Python? It's almost always https:// in the proxy URL. The one-character fix, plus every SSLEOFError cause.
pip install Proxy Setup: --proxy, pip.conf and Env Vars
pip install proxy setup that works: the --proxy flag, pip.conf, PIP_PROXY vs HTTPS_PROXY precedence, encoded passwords, and the SSL error a wrong scheme causes.
Proxy Not Working on Certain Websites? How to Fix It
Proxy not working on certain websites but fine everywhere else? Tell a block from a port, TLS/SNI or DNS fault with one command each, then apply the right fix.
