β Blog Β· May 13, 2026 Β· networking, performance
MTU and MSS: Why Your VPN Feels Slow
You set up a VPN, run a speed test β fine. You SSH into a remote host β fine. Then you try to git clone a real repo and the connection hangs at 99% forever. Big page on the corporate intranet, same story. Small requests work. Anything over a few kilobytes stalls. This is almost always MTU.
MTU vs MSS
MTU (Maximum Transmission Unit) is the largest IP packet a link will carry without fragmentation. On standard Ethernet it's 1500 bytes. That number includes the IP header (20 bytes for v4, 40 for v6) and TCP header (20 bytes).
MSS (Maximum Segment Size) is the largest TCP payload per segment. On standard Ethernet with IPv4 + TCP:
MSS = MTU β IP header β TCP header = 1500 β 20 β 20 = 1460 bytesMSS is announced in the TCP SYN. Both ends compare and use the smaller. So far, normal. The problem is what happens when a tunnel sits in the path.
Tunnel overhead
Every encapsulation eats a few bytes:
- PPPoE (DSL): 8 bytes β MTU 1492
- GRE: 24 bytes β MTU 1476
- IPsec ESP tunnel (AES-GCM): ~50β60 bytes β MTU ~1440
- IPsec ESP transport (AES-CBC): ~30β40 bytes β MTU ~1460
- WireGuard: 60 bytes β MTU 1420 (default)
- VXLAN over IPv4: 50 bytes β MTU 1450
- Double-encapsulated (e.g., IPsec + GRE): 70+ bytes β MTU under 1430
The kernel needs to know the tunnel's reduced MTU. If it doesn't, large packets get dropped or fragmented downstream, and TCP retransmits forever.
Why small requests work but big ones don't
TCP sends small payloads in single packets that fit comfortably under any reasonable MTU. Curl a 1KB API response β works. Transfer a 100KB file and the OS will try to send 1460-byte segments, which after tunnel encapsulation exceed the path MTU. A router in the middle either fragments (slow) or drops with an ICMP "Fragmentation Needed" (type 3, code 4). If firewalls block that ICMP β which they often do β the sender just retransmits, gets nowhere, and the connection stalls.
This is called a PMTUD black hole. The Path MTU Discovery mechanism is broken because someone filtered the ICMP packets that make it work.
MSS clamping
The fix is MSS clamping. The tunnel device rewrites the MSS option in TCP SYN packets going through it, advertising a lower MSS that fits the tunnel. On Linux:
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtuOr pin a specific value:
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1360For WireGuard with default MTU 1420 over IPv4: MSS = 1420 β 20 β 20 = 1380. Allow a bit of safety margin: 1360 is the common pick. Use the MTU/MSS calculator to compute the right value for any stack.
How to tell if MTU is your problem
Run a ping with the Don't Fragment bit set and a large payload:
ping -M do -s 1472 peer-ip # Linuxping -D -s 1472 peer-ip # macOSping -f -l 1472 peer-ip # Windows1472 + 8 (ICMP header) + 20 (IP header) = 1500 bytes, the standard Ethernet MTU. If that fails but -s 1372 succeeds, you have a smaller path MTU somewhere β probably a tunnel. Binary search to find the exact threshold.
Jumbo frames
Some environments β data center fabrics, storage networks β push MTU up to 9000 bytes ("jumbo frames"). NFS over jumbo is dramatically faster because each packet carries six times more payload. But every device in the path has to agree, and one misconfigured switch will quietly fragment and ruin your day. Test with the DF-ping above.