How to fix kex_exchange_identification: Connection closed by remote host in Git
The Git server closed the SSH connection during the initial identification/banner exchange. On GitHub this is almost always IP-based rate limiting from a shared or NAT'd address, but blocked SSH ports can also cause it.
Quick fix
Fixes kex_exchange_identification: Connection closed by remote host
ssh -vT git@github.com- Confirm the failure happens before authentication
- Wait and retry, then reduce your connection rate
- Check whether outbound SSH port 22 is reachable
- Use SSH over HTTPS port 443 when port 22 is blocked
ssh -vT git@github.comkex_exchange_identification: Connection closed by remote host
What this error means
`kex_exchange_identification: Connection closed by remote host` means the SSH server dropped your connection during the very first stage of the handshake — before key exchange completes and before any authentication happens. The server (or something in front of it) sent a TCP close instead of an SSH protocol banner. Because this failure occurs before authentication, it is not caused by a missing or misconfigured SSH key, and keep-alive settings cannot prevent it (there is no established session yet to keep alive). Against the large hosted providers (GitHub, GitLab, Bitbucket), the most common reason by far is server-side rate limiting / abuse throttling tied to your public IP address. Many users behind the same NAT gateway, VPN exit node, corporate proxy, or cloud/CI provider share one outbound IP, and the combined connection rate can trip the provider's protection, which then briefly refuses new SSH connections from that IP. GitHub explicitly documents this behavior for `kex_exchange_identification: Connection closed by remote host`. Other causes are connectivity-related: a firewall or proxy blocking outbound TCP port 22, the SSH endpoint being temporarily unreachable, or — for self-hosted servers you control — `sshd` rejecting the connection early (for example via `MaxStartups` throttling or resource exhaustion).
Typical symptoms
- Git clone, push, pull, or fetch over SSH fails immediately with "kex_exchange_identification: Connection closed by remote host"
- ssh -T git@github.com fails the same way before asking for a passphrase or showing a welcome message
- The failure is intermittent and often succeeds after waiting a minute and retrying
- It happens on a shared/office/VPN network or on a CI runner, but works fine from a home connection
- HTTPS clones work while SSH does not
- Many machines behind the same public IP all start failing around the same time
Common causes
- Server-side IP rate limiting / abuse throttling, commonly because many users share one outbound IP (corporate NAT, VPN, proxy, or a cloud/CI provider's egress address)
- A firewall or network proxy blocking outbound SSH on TCP port 22
- Transient unavailability or a brief provider-side incident on the SSH endpoint
- A misbehaving local SSH proxy/ProxyCommand or VPN that drops the connection before the banner is exchanged
- On a self-hosted server you control: sshd connection throttling (MaxStartups), too many simultaneous unauthenticated connections, or resource exhaustion closing connections early
How to fix "kex_exchange_identification: Connection closed by remote host"
Run a verbose SSH test so you can see exactly where the connection dies:
ssh -vT git@github.comIf the output ends shortly after Connecting to github.com ... with kex_exchange_identification: Connection closed by remote host — before any line about offering a public key or a passphrase prompt — then the server closed the connection during the initial handshake. This rules out SSH key problems and idle-timeout problems, and points to rate limiting or a network block.
Because the dominant cause on hosted providers is per-IP rate limiting, the simplest and most effective fix is to wait about a minute and try again:
sleep 60 && ssh -T git@github.comIf you are hitting this from CI or a script that opens many Git connections rapidly, lower the rate instead of hammering the server:
- Avoid running many parallel git fetch/git clone jobs that all share one egress IP.
- Add a short backoff-and-retry around Git steps rather than retrying instantly.
- Reuse a single SSH connection for multiple Git operations with connection multiplexing in ~/.ssh/config:
Host github.com
ControlMaster auto
ControlPath ~/.ssh/cm-%r@%h:%p
ControlPersist 60sIf an entire office or many CI jobs share one NAT'd public IP, the combined rate can trip the limit even when your own usage is light — in that case the network's shared IP is the real culprit (see step 4).
If retries never succeed, your network may be blocking SSH. Test raw TCP reachability to port 22:
nc -vz github.com 22
# or
ssh -vT git@github.comA connection that never establishes (rather than one that establishes and is then closed) points to a firewall/proxy blocking port 22.
GitHub (and some other providers) expose SSH on port 443, which is rarely blocked. Test it:
ssh -T -p 443 git@ssh.github.comIf that succeeds, make it permanent in ~/.ssh/config:
Host github.com
Hostname ssh.github.com
Port 443
User gitNote that switching networks (sharing a different egress IP) can also be what gets you past a rate limit, not only the port change.
A misconfigured local SSH proxy or a VPN can sever the connection before the banner is exchanged. Inspect your effective SSH settings:
ssh -G github.com | grep -i -E 'proxy|hostname|port'If a ProxyCommand or ProxyJump is set that you do not expect, comment it out and retry. If you are on a VPN, temporarily disconnect and test again:
ssh -T git@github.comIf it works without the VPN, the VPN's exit IP is likely rate limited or its routing/inspection is dropping the connection — use split tunneling to send Git traffic outside the VPN, or use HTTPS for Git.
HTTPS uses port 443 and a separate path that is not subject to the same SSH handshake throttling, so it is a reliable workaround:
# See current remotes
git remote -v
# Switch this repo's remote to HTTPS
git remote set-url origin https://github.com/<owner>/<repo>.gitAuthenticate with a personal access token (not a password). Use a credential helper that stores credentials securely for your platform rather than plaintext, for example:
# macOS
git config --global credential.helper osxkeychain
# Windows
git config --global credential.helper manager
# Linux (libsecret-backed)
git config --global credential.helper libsecretFor a self-hosted Git/SSH server you control, the early close usually comes from connection throttling or resource limits. Check the auth logs for the rejection reason:
sudo journalctl -u ssh -f # systemd
sudo tail -f /var/log/auth.log # Debian/Ubuntu
sudo tail -f /var/log/secure # RHEL/CentOSIf you see connections being dropped before authentication under load, review MaxStartups and MaxSessions in /etc/ssh/sshd_config. The default MaxStartups 10:30:100 starts randomly dropping unauthenticated connections once 10 are pending. Raise it cautiously if legitimate concurrent traffic is being throttled, then reload:
sudo sshd -t && sudo systemctl reload sshAlso confirm the host is not out of memory or file descriptors, which can cause sshd to close connections early:
free -h
ss -sAdvanced notes
Why keep-alive does not help: ServerAliveInterval/ServerAliveCountMax only affect an already-established SSH session by sending keepalive packets to prevent idle disconnects. The kex_exchange_identification: Connection closed by remote host error happens before the session exists, so these settings have no effect on it.
Why this is not a host-key problem: the error appears before host-key verification, so editing or wiping ~/.ssh/known_hosts will not fix it (a genuine host-key change reports a different, explicit warning). Avoid bulk-deleting known_hosts — that removes verification for every host you trust and invites man-in-the-middle attacks. If you ever must remove a single stale entry, scope it precisely: ssh-keygen -R github.com.
CI/CD specifics: hosted runners (GitHub-hosted, GitLab shared runners, etc.) and cloud egress gateways share IP pools across many tenants, so you can be throttled by activity that is not yours. Mitigations: cache dependencies and avoid re-cloning, reuse a single SSH connection via ControlMaster/ControlPersist, add bounded exponential backoff with retries around clone/fetch, prefer shallow clones (--depth=1) where history is not needed, and consider HTTPS with a token for fetch-only steps.
Corporate networks: security appliances doing deep packet inspection or TLS/SSH interception can terminate connections during the handshake. The correct fix is to have your network team allowlist the provider's SSH endpoints or permit SSH-over-443 — not to disable any verification.
Frequently asked questions
- What does "kex_exchange_identification: Connection closed by remote host" mean?
- kex_exchange_identification: Connection closed by remote host means the SSH server dropped your connection during the very first stage of the handshake — before key exchange completes and before any authentication happens. The server (or something in front of it) sent a TCP close instead of an SSH protocol banner. Because this failure occurs before authentication, it is not caused by a missing or misconfigured SSH key, and keep-alive settings cannot prevent it (there is no established session yet to keep alive).
- How do I fix "kex_exchange_identification: Connection closed by remote host"?
- The Git server closed the SSH connection during the initial identification/banner exchange. On GitHub this is almost always IP-based rate limiting from a shared or NAT'd address, but blocked SSH ports can also cause it.
- What causes "kex_exchange_identification: Connection closed by remote host"?
- Server-side IP rate limiting / abuse throttling, commonly because many users share one outbound IP (corporate NAT, VPN, proxy, or a cloud/CI provider's egress address) A firewall or network proxy blocking outbound SSH on TCP port 22 Transient unavailability or a brief provider-side incident on the SSH endpoint
- Is the "kex_exchange_identification: Connection closed by remote host" error serious?
- It is a medium-severity error — more of a blocker than a risk. The steps above resolve it.
Official resources & further reading
- 📚Git Official Documentation
- 📖Git: SSH Configuration(Official Docs)
- 💬Stack Overflow: Git SSH(Stack Overflow)