Bali, Indonesia
Insights

Why SSH Works but Large Downloads Hang on Your Linux Server

October 20, 2026
0:00
You can SSH into the server, run commands, and curl -I the site without trouble. But when you scp a backup, download a large file, or load a heavy page, the transfer starts, stalls after a few kilobytes, and then sits there with no error until something times out. When small requests work and big ones hang, the network path is usually dropping large packets while letting small ones through. The job is to find out why, because the fix depends on which of three causes you are dealing with.
Every network link has an MTU (Maximum Transmission Unit), the largest packet it can carry in one piece. On ordinary Ethernet that is 1500 bytes. TCP sizes its segments to fit, using an MSS (Maximum Segment Size) that is normally the MTU minus the IP and TCP headers. On IPv4 with a 1500-byte MTU, that leaves 1460 bytes of data per segment. Modern TCP stacks set the Don't Fragment (DF) bit on their packets. If a router along the path has a smaller next link, it can't split the packet. It drops the packet and sends back an ICMP Type 3 Code 4 message, "Fragmentation Needed", which includes the MTU it can handle. The sender lowers its packet size and tries again. This process is called Path MTU Discovery (PMTUD). IPv6 works the same way with an ICMPv6 "Packet Too Big" message, and IPv6 routers never fragment at all. This explains the symptom. The TCP handshake, SSH keystrokes, and HTTP headers are all small packets. They fit through any link, so the connection looks healthy. The first full-size data segment is the first packet that doesn't fit. If it is dropped and the sender never learns why, TCP keeps retransmitting the same oversized segment, and the transfer freezes. This is also why "SSH works, so the network is fine" is a common misreading. An interactive session rarely sends anything large enough to hit the limit.
The first cause is a local MTU mismatch. Your server's interface believes the link carries 1500 bytes, but the network underneath carries less. This happens with tunnels, VPNs, PPPoE uplinks (commonly 1492), and some overlay or virtualized networks that add their own headers. Oversized frames are dropped at layer 2. No router is involved, so nothing sends an ICMP message. Allowing ICMP won't help here, because no ICMP message is ever generated. The second cause is a PMTUD black hole. A router somewhere on the path has a smaller link ahead, often because traffic enters an encapsulated tunnel. GRE or IPsec headers can add roughly 20 to 50 bytes of overhead. The router sends Fragmentation Needed correctly, but a firewall filters it on the way back. Some operators block all ICMP as a security habit. For downloads, the sender is your server, so the ICMP message has to get back to your server. A strict inbound firewall on the server itself is a frequent cause, as is a security group or edge firewall in front of it. For the tunnel-specific version of this problem, see When Keepalives Vanish: Debugging IPsec MTU Black Holes Across Cloud Transit Gateways. The third possibility is that MTU has nothing to do with it. Packet loss under load, rate limiting, a full connection tracking table, NIC offload quirks on virtual machines, or a slow application can all produce transfers that start and then stall. You only land here after the MTU tests come back clean, and the next steps are different. When a customer reports that the site is slow but SSH is fine, a good first step for support is to rule MTU in or out with a two-minute test. Restarting services or tuning PHP comes later.
You need two tools: ping with fragmentation disallowed, and tracepath. Run them from the server toward the client side, and if you can, also from the client toward the server, because the path and its filtering can differ by direction. The addresses below are documentation placeholders. Replace them with your own.
BASH
# Show the MTU each interface thinks it has (read-only)
ip link show

# IPv4: full 1500-byte packet, Don't Fragment set
# 1472 payload + 8 ICMP header + 20 IP header = 1500
ping -c 3 -M do -s 1472 203.0.113.10

# Step down to find the size that actually gets through
ping -c 3 -M do -s 1400 203.0.113.10

# IPv6: 1452 payload + 8 ICMPv6 header + 40 IPv6 header = 1500
ping -6 -c 3 -M do -s 1452 2001:db8::10

# Per-hop path MTU as seen from this host
tracepath -n 203.0.113.10

# During a stalled transfer: MSS, retransmits, and RTT for the socket
ss -ti dst 203.0.113.10
Reading the results is mostly a matter of what ping does at 1472. If the 1472-byte ping fails even to your default gateway, but smaller sizes work, the mismatch is on your own link. Compare the size that works with what ip link show reports. A gap between the two means your interface MTU is wrong for the network it sits on. If the gateway is fine and a remote host fails, watch what ping prints. If you get a reply saying fragmentation is needed, with an MTU value, PMTUD is working. Some hop has a smaller MTU and is reporting it, so TCP should adapt on its own. If large pings simply time out while small ones succeed, and nothing comes back, the router's ICMP message is being lost somewhere. That is the black hole pattern. tracepath adds per-hop detail. It reports a pmtu value as it goes and prints the discovered path MTU at the end. If the reported value drops at a specific hop, that hop is where the path narrows. If tracepath shows 1500 throughout but large pings still vanish, the narrowing happens somewhere that doesn't report it. A few caveats. ICMP echo can be rate limited or blocked separately from other ICMP types, so a total ping failure doesn't prove anything about MTU. Compare sizes against the same target and look at the pattern. Also, on a stalled connection ss -ti shows the mss in use and a growing retrans count. An MSS of 1460 combined with retransmits that keep climbing is consistent with full-size segments disappearing.
Once you know which cause you have, the choice mostly comes down to what you control and how much risk you accept.
CauseFix that fitsMain trade-off
Local MTU mismatchSet the interface MTU to what the network actually carriesApplies to all traffic. Must be made persistent the right way for your distro
ICMP filtered on a firewall you controlAllow ICMP Fragmentation Needed (and ICMPv6 Packet Too Big)Correct fix, but needs a firewall change and review
ICMP filtered somewhere you don't controlMSS clamping on your gateway, or lower MTU on your sideWorks around the problem. MSS clamping only helps TCP
For a local mismatch, fix the MTU at the source. Lowering it on the live interface with ip link set takes effect right away. Doing it over SSH on the wrong interface can cut your session, so have console access ready. The persistent setting lives in netplan, NetworkManager, ifcfg files, or cloud-init depending on the image, so check how your server is configured before editing anything. For a black hole on a firewall you own, the clean answer is to stop dropping ICMP errors. Blocking all ICMP breaks PMTUD and adds little security. If you want to filter ICMP, filter echo requests and keep the error types. Many stateful rule sets already accept ICMP errors that belong to an existing connection through a RELATED rule. Check that your rules actually include that, and that an earlier blanket drop doesn't override it. If the filtering happens upstream, at a provider edge or the client's network, you can't fix it there. MSS clamping rewrites the MSS in TCP handshakes passing through a Linux router or gateway, so both ends agree on segments small enough to fit without needing PMTUD. It is a well-established workaround on tunnel endpoints. It does nothing for UDP. Another option on Linux hosts is the net.ipv4.tcp_mtu_probing sysctl, which lets TCP detect the black hole itself by probing with smaller segments after repeated loss. Treat it as a fallback, not a replacement for fixing the path.
If 1472-byte DF pings pass in both directions, tracepath reports 1500 end to end, and transfers still stall, stop working on MTU. Look at ss -ti during a stall. Steady retransmits at normal segment sizes point to ordinary loss or congestion. A connection that is idle with no retransmits points to the application or the disk. If the stall only happens in one direction, or only between certain networks, asymmetric routing and stateful filtering become likely. That is covered in Deciding If Inter-VPC Timeouts Are Asymmetric Drops or a Dead Path. The practical rule: when small things work and big things hang, run a DF ping at full size before anything else. If it fails at your gateway, correct your interface MTU. If it fails further out with no ICMP reply, find the firewall eating Fragmentation Needed, and clamp MSS if that firewall isn't yours. If it passes, the problem is somewhere else, and you have saved yourself an afternoon of MTU tuning that would not have helped.