kex_exchange_identification: Connection Closed by Remote Host
This message places the failure in SSH's early pre-authentication path, but it does not prove cryptographic key exchange actually began. The connection can close around SSH identification/banner exchange or later pre-auth processing before normal user authentication. Capture the last successful protocol step, then separate sshd/listener state, unauthenticated-connection pressure, source controls and the network path before touching credentials.
kex_exchange_identification: Connection closed by remote host or a closely related early disconnect.The connection dies before you reach the expected password/public-key authentication result.You need to distinguish server pressure or blocking from a client-key problem.ssh -vvv user@serverKeep the lines showing the target address/port, whether TCP connected, and the final messages before the close. If you administer the server, correlate the same timestamp with sshd/security logs.
Verify: Establish whether the failure occurs before authentication is offered. If verbose output reaches normal authentication and fails there, follow the authentication evidence instead.
Why this error does not identify one root cause
The client knows the connection closed in the early SSH pre-auth path; the label kex_exchange_identification is not proof that algorithm negotiation or key exchange completed—or even necessarily began. OpenSSH's MaxStartups limits concurrent unauthenticated connections, while per-source controls/penalties and external firewalls, intrusion-prevention systems, proxies, load balancers, server resource problems or sshd itself can close a connection at a similar client-visible boundary.
That is why changing a key or password is usually premature: those credentials may not have been evaluated yet.
Fix path #1 — correlate the server event before changing limits
If you control the server, inspect SSH and security logs at the exact client timestamp. Look for evidence of dropped unauthenticated connections, source blocking/penalties, daemon errors, or an upstream control closing the socket.
If failures are intermittent and coincide with bursts of new SSH connections, inspect the effective MaxStartups policy and contemporaneous unauthenticated-connection pressure. The documented default is 10:30:100, but a default value is background information—not evidence that MaxStartups caused this connection. Included configuration and server policy may also change the effective value; inspect the server's loaded pre-auth configuration rather than assuming the packaged default is active.
Verify: The server log or connection-pressure evidence lines up with the failed client timestamp and the behavior changes when the proven condition is removed—not merely after an unrelated retry.
Fix path #2 — separate source blocking from server-wide failure
Compare controlled tests rather than disabling protection globally. If one source/network consistently fails while another approved network succeeds, record the source identity the server actually sees as well as the client-side network. NAT, VPN, proxies and shared egress can make multiple clients appear as one public source—or make one client appear differently. Distinguish OpenSSH's concurrent per-source startup limit (PerSourceMaxStartups) from PerSourcePenalties, which can temporarily refuse source ranges after specified pre-auth/authentication conditions. Investigate those separately from firewall/rate-limit/ban/proxy/NAT controls. If every independent source fails, prioritize sshd/listener/server health and shared upstream infrastructure.
Do not disable Fail2ban, a firewall, MFA, or another security control globally just to test. Prefer logs, rule inspection, a narrowly scoped approved exception, or an alternate authorized network test.
Verify: You can explain why one path/source is rejected and another succeeds, with corresponding server/network evidence.
Check sshd safely when the server itself is suspect
If logs point to sshd configuration or service state, inspect service logs and the loaded pre-auth configuration before restarting. Do not infer the active MaxStartups/PerSourceMaxStartups/PerSourcePenalties policy from a packaged default or one grepped line; account-specific authentication Match logic is a different stage and should not be used to explain a connection that is being dropped before user authentication. After any justified configuration edit, keep an existing administrative session open and validate first:
sudo sshd -tVerify: Validation is clean and a separate new connection progresses beyond the original pre-auth boundary into the expected authentication stage. If it now fails with a concrete authentication error, stop diagnosing kex_exchange_identification and follow that new failure class. Keep the existing admin session until the new path is proven.
If this happens during GitHub Git operations
Reproduce the transport independently with ssh -vT git@github.com. GitHub documents that corporate firewalls, proxies, or networks can block SSH connectivity. If port 22 is blocked and SSH is still required, GitHub officially supports testing SSH over port 443 using ssh.github.com for GitHub.com accounts.
ssh -T -p 443 git@ssh.github.comUse GitHub's documented hostname/configuration and verify published host-key fingerprints. Do not hard-code an IP copied from a forum or silently trust a new host key.
Verify: The official port-443 test succeeds on the same network where port 22 fails, which supports a network-path diagnosis rather than a repository or SSH-key diagnosis.
What not to do first
MaxStartups without evidence of unauthenticated-connection pressure.Do not globally disable Fail2ban, firewalls, rate limits, source penalties or other security controls.Do not add random service IPs to /etc/hosts as a connectivity workaround.Do not append an unverified host key with ssh-keyscan merely to silence trust warnings.Do not restart/reload sshd after configuration changes until sshd -t succeeds, and keep a working admin session open.Do not read the function name kex_exchange_identification as proof that cryptographic key exchange actually started or completed.Do not blame MaxStartups from its documented default alone; correlate the loaded policy with real unauthenticated-connection pressure and the failed timestamp.Do not conflate PerSourceMaxStartups with PerSourcePenalties: one limits concurrent unauthenticated starts per source, while the other can temporarily refuse sources after configured conditions.Do not use per-user authentication Match logic to explain a connection that is being dropped before user authentication is reached.Do not equate the client's local IP/network with the source identity seen by the server when NAT, VPN, proxies or shared egress are involved.Do not keep diagnosing the pre-auth close once a new connection reaches authentication and produces a more specific error.Still failing? Capture this evidence
ssh -vvvtarget hostname/IP and port, redacted where requiredwhether TCP connects and whether authentication is ever offeredclient/server OpenSSH versionsserver sshd/security logs at the same timestampeffective MaxStartups/per-source policy if you administer the serverwhether the result changes from another authorized network/sourcewhether all users/sources fail or only one path doesfor GitHub, results of the official port-22 and port-443 SSH teststhe last successful SSH protocol step before close (TCP connect, identification/banner exchange, or later pre-auth evidence)the loaded MaxStartups, PerSourceMaxStartups and PerSourcePenalties settings rather than only packaged/default valuesthe source IP/identity observed by the server or security control when NAT/VPN/proxying is relevantthe first different authentication error if the connection begins progressing past pre-authReferences
Still stuck? Ask the community
Share the redacted failure boundary, whether authentication is reached, and matching server/network evidence.
Loading community discussion…