PuTTY's "Server unexpectedly closed network connection" most often means sshd dropped you at connection setup (fail2ban ban, MaxStartups, TCP wrappers, cipher/kex mismatch); idle timeouts are a less common cause.
This message comes from PuTTY (and PuTTY-derived clients like WinSCP) and means the TCP connection to the SSH server was closed by the remote end without a clean SSH protocol disconnect. Despite the word "unexpectedly," the most common trigger is the server deliberately dropping the connection during setup — before or during the key exchange and authentication handshake. Intrusion-prevention tools such as fail2ban or sshguard ban your IP, the daemon's MaxStartups limit sheds unauthenticated connections under load, TCP wrappers (hosts.deny/hosts.allow) refuse you, or the client and server share no common key-exchange, cipher, or host-key algorithm and sshd closes the socket. A separate, less common scenario produces the same words: an already-established session is torn down after a period of inactivity. Here a firewall/NAT device times out the idle TCP flow, or the server's idle/keepalive settings expire the session while no traffic flows. Because the same message covers both a handshake-time rejection and an idle-time drop, the first job is to determine which one you are seeing — a fix for one (for example keepalives) does nothing for the other.
This message has two very different causes, and fixing the wrong one wastes time. First classify the failure.
- Does it fail immediately on connect (before or just after the password/key prompt)? That is a handshake-time drop — go to Steps 2-5.
- Does an established session drop only after sitting idle (minutes)? That is an idle-timeout drop — go to Steps 6-7.
Reproduce with a verbose OpenSSH client (even on Windows; PuTTY itself shows little detail):
ssh -vvv user@hostKey signals:
- Connection closed by <ip> port 22 very early, or right after expecting SSH2_MSG_KEX... = setup-time rejection (ban, MaxStartups, TCP wrappers).
- Unable to negotiate with <ip>: no matching key exchange method (or cipher/host key) = algorithm mismatch.
- A session that stays up under -vvv until you stop typing, then drops = idle timeout.
If you control the server, watch its log live while you connect:
sudo tail -f /var/log/auth.log | grep sshd # Debian/Ubuntu
sudo journalctl -u ssh -f # systemd journalRepeated failed logins are the most common reason a server drops you at setup. On the server:
# fail2ban: list jails, then list banned IPs for the sshd jail
sudo fail2ban-client status
sudo fail2ban-client status sshd
# Unban your address if listed
sudo fail2ban-client set sshd unbanip <your_ip>If sshguard is used instead, banned addresses live in its backend (often nftables/ipset):
sudo nft list set inet sshguard attackers 2>/dev/nullAlso confirm you are not blocked at the firewall:
sudo iptables -L -n | grep <your_ip>To stop getting banned, fix the underlying auth failures (wrong key, wrong username) rather than disabling the protection. If you must connect during a ban, do so from a different IP or whitelist your address in jail.local (ignoreip).
If the failure correlates with many simultaneous connections, sshd may be shedding unauthenticated sessions. On the server:
sudo grep -i maxstartups /etc/ssh/sshd_configThe value is start:rate:full. The OpenSSH default 10:30:100 means: begin randomly dropping new unauthenticated connections at 10 in-flight (30% chance, rising linearly) and reject all once 100 are pending. For high-concurrency hosts (CI, bastions), raise it:
# in /etc/ssh/sshd_config
MaxStartups 100:30:500Validate and reload:
sudo sshd -t && sudo systemctl reload sshdReducing repeated reconnect storms from clients (e.g. broken automation retry loops) also helps.
The server may be refusing your IP or user by policy, which looks like an unexpected close. On the server, inspect access controls:
# TCP wrappers (still honored if sshd is built with libwrap)
sudo cat /etc/hosts.deny
sudo cat /etc/hosts.allow
# sshd access directives
sudo grep -Ei 'AllowUsers|DenyUsers|AllowGroups|DenyGroups|^Match' /etc/ssh/sshd_configIf a hosts.deny rule like sshd: ALL or a DenyUsers/AllowUsers line excludes you, adjust it to permit your source address or account, then:
sudo sshd -t && sudo systemctl reload sshdPrefer narrowing an AllowUsers/hosts.allow entry to your specific address over opening access broadly.
Connecting from a modern client to a very old server (or vice versa) can leave no common algorithm, and sshd closes the socket. The OpenSSH client makes this explicit:
ssh -vvv user@host 2>&1 | grep -i 'no matching'List what each side offers:
ssh -Q kex; ssh -Q cipher; ssh -Q key # client capabilities
sudo sshd -T | grep -Ei 'kexalgorithms|ciphers|hostkeyalgorithms' # server (run on server)The correct long-term fix is to upgrade the older side so both support modern algorithms. If you must reach a legacy host temporarily, you can re-enable a specific legacy algorithm for that host only in ~/.ssh/config, understanding it weakens that connection's security:
Host legacy-box
HostName host
# Last resort: only for an old server you cannot upgrade yet.
KexAlgorithms +diffie-hellman-group14-sha1
HostKeyAlgorithms +ssh-rsa
PubkeyAcceptedAlgorithms +ssh-rsaRemove these overrides once the server is updated. Avoid globally lowering your client's algorithms with Host *.
If Step 1 showed an established session dropping after idle time, keepalives usually fix it. Add to ~/.ssh/config:
Host *
ServerAliveInterval 60
ServerAliveCountMax 3Or per-connection:
ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 user@hostThis sends an encrypted keepalive every 60 seconds and tolerates 3 unanswered replies (~180s) before giving up. Behind an aggressive firewall/NAT, shorten the interval (e.g. 30) so it stays under the device's idle timeout. In PuTTY, the equivalent is Connection > 'Seconds between keepalives' (set to 30-60). Note: keepalives do nothing for the setup-time drops in Steps 2-5.
If you administer the server and idle sessions are being dropped, set client-alive options in /etc/ssh/sshd_config:
ClientAliveInterval 60
ClientAliveCountMax 3
TCPKeepAlive yesClientAliveInterval makes sshd probe an idle client every 60s; ClientAliveCountMax allows 3 missed replies before disconnecting. Validate and reload:
sudo sshd -t && sudo systemctl reload sshdIf the timeout is actually imposed by an intermediate firewall/NAT rather than sshd, server keepalive plus the client ServerAliveInterval from Step 6 keeps the flow active so the device does not reap it.
Two keepalive layers: TCPKeepAlive operates at the OS socket level (probes can be spoofed/dropped by middleboxes), while ClientAliveInterval/ServerAliveInterval send encrypted SSH-protocol probes. For idle-timeout problems, prefer the application-level options; enable both for maximum compatibility.
Reading the log line: Connection closed by authenticating user ... [preauth] indicates the failure happened before authentication completed — consistent with the setup-time causes (ban, MaxStartups, TCP wrappers, mismatch) rather than an idle drop.
Legacy-algorithm caveat: Re-enabling diffie-hellman-group14-sha1 or ssh-rsa (SHA-1) is a temporary bridge for un-upgradable hosts only, and should be scoped to a single Host block, never Host *. SHA-1 host-key signatures and small DH groups are deprecated; upgrade the server and remove the override.
Cloud bastions and load balancers: AWS NLB/ELB, GCP, and Azure idle timeouts (often 5-20 minutes, and some are non-configurable) cause the idle-timeout variant on otherwise healthy connections; set ServerAliveInterval below the balancer's timeout.
Long-running commands: For jobs that produce no output for long stretches, run them inside tmux/screen so a dropped client connection does not kill the work, and reattach after reconnecting.
sign_and_send_pubkey: no mutual signature supported
How to fix "sign_and_send_pubkey: no mutual signature supported" in SSH
sign_and_send_pubkey: signing failed for RSA from agent: agent refused operation
How to fix "sign_and_send_pubkey: signing failed for RSA from agent: agent refused operation" in SSH
Bad owner or permissions on /home/user/.ssh/config
How to fix "Bad owner or permissions on .ssh/config" in SSH
No more authentication methods to try.
How to fix "No more authentication methods to try." in SSH
Error connecting to agent: Connection refused
How to fix "Error connecting to agent: Connection refused" in SSH