How to fix kex_exchange_identification: Connection closed by remote host in Git

GitINTERMEDIATEMEDIUMUpdated May 28, 2026· 260 views

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
  1. Confirm the failure happens before authentication
  2. Wait and retry, then reduce your connection rate
  3. Check whether outbound SSH port 22 is reachable
  4. Use SSH over HTTPS port 443 when port 22 is blocked
git — error
ssh -vT git@github.com
kex_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

  1. 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)
  2. A firewall or network proxy blocking outbound SSH on TCP port 22
  3. Transient unavailability or a brief provider-side incident on the SSH endpoint
  4. A misbehaving local SSH proxy/ProxyCommand or VPN that drops the connection before the banner is exchanged
  5. 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"

1Confirm the failure happens before authentication

Run a verbose SSH test so you can see exactly where the connection dies:

bash
ssh -vT git@github.com

If 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.

2Wait and retry, then reduce your connection rate

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:

bash
sleep 60 && ssh -T git@github.com

If 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:

text
Host github.com
    ControlMaster auto
    ControlPath ~/.ssh/cm-%r@%h:%p
    ControlPersist 60s

If 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).

3Check whether outbound SSH port 22 is reachable

If retries never succeed, your network may be blocking SSH. Test raw TCP reachability to port 22:

bash
nc -vz github.com 22
# or
ssh -vT git@github.com

A connection that never establishes (rather than one that establishes and is then closed) points to a firewall/proxy blocking port 22.

4Use SSH over HTTPS port 443 when port 22 is blocked

GitHub (and some other providers) expose SSH on port 443, which is rarely blocked. Test it:

bash
ssh -T -p 443 git@ssh.github.com

If that succeeds, make it permanent in ~/.ssh/config:

text
Host github.com
    Hostname ssh.github.com
    Port 443
    User git

Note that switching networks (sharing a different egress IP) can also be what gets you past a rate limit, not only the port change.

5Rule out a local proxy, ProxyCommand, or VPN

A misconfigured local SSH proxy or a VPN can sever the connection before the banner is exchanged. Inspect your effective SSH settings:

bash
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:

bash
ssh -T git@github.com

If 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.

6Fall back to HTTPS for Git operations

HTTPS uses port 443 and a separate path that is not subject to the same SSH handshake throttling, so it is a reliable workaround:

bash
# See current remotes
git remote -v

# Switch this repo's remote to HTTPS
git remote set-url origin https://github.com/<owner>/<repo>.git

Authenticate with a personal access token (not a password). Use a credential helper that stores credentials securely for your platform rather than plaintext, for example:

bash
# macOS
git config --global credential.helper osxkeychain
# Windows
git config --global credential.helper manager
# Linux (libsecret-backed)
git config --global credential.helper libsecret
7If you run the Git server: check sshd throttling and logs

For 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:

bash
sudo journalctl -u ssh -f          # systemd
sudo tail -f /var/log/auth.log     # Debian/Ubuntu
sudo tail -f /var/log/secure       # RHEL/CentOS

If 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:

bash
sudo sshd -t && sudo systemctl reload ssh

Also confirm the host is not out of memory or file descriptors, which can cause sshd to close connections early:

bash
free -h
ss -s

Advanced 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