Frontend Labs
⋯

Chapter 15 — MTU: the maximum size of a packet

In one sentence

Every link has a maximum packet size (usually 1500 bytes), and when a packet is too big for the next link, something has to give — this causes one of the most confusing bugs in networking.

The problem

You send a 1 MB file. It can't go as one packet — Ethernet's payload maxes out at 1500 bytes (Chapter 5). So it's split into ~700 packets. Fine. But what if a link somewhere along the path has a smaller limit than yours?

The key numbers

   MTU  (Maximum Transmission Unit)  = the biggest IP packet a link will carry
   MSS  (Maximum Segment Size)       = the biggest chunk of DATA inside it

   Standard Ethernet:   MTU = 1500
                        - 20 bytes IP header
                        - 20 bytes TCP header
                        ---------------------
                        MSS = 1460 bytes of your actual data

Common MTUs you'll meet:

Link MTU
Standard Ethernet / Wi-Fi 1500
PPPoE (some DSL) 1492
WireGuard VPN 1420
IPsec VPN ~1400
Cloud overlay networks (VXLAN) 1450
Data-centre "jumbo frames" 9000
IPv6 minimum every link must support 1280

Notice the VPN entries. A VPN wraps your packet inside another packet, and that wrapper costs bytes. This is why VPNs cause MTU problems so often.

What happens when a packet is too big

Two possibilities:

   CASE 1: the sender said "you may split this" (DF bit not set)
     -> the router chops it into FRAGMENTS
     -> slow, fragile, and often blocked by firewalls

   CASE 2: the sender said "DO NOT FRAGMENT" (DF bit set -- TCP always does this)
     -> the router DROPS the packet
     -> and sends back ICMP "Fragmentation Needed", saying the correct size
     -> the sender shrinks its packets and retries

Case 2 is called Path MTU Discovery (PMTUD), and it works well — as long as that ICMP message gets through.

The classic bug: the PMTU black hole

Someone blocks all ICMP on a firewall "for security" (Chapter 14). Now:

   1. TCP handshake works fine        (those packets are tiny)
   2. Your request works fine          (also small)
   3. The server sends a full-size 1500-byte response packet
   4. A router on the path can't fit it, drops it, sends ICMP...
   5. ...which is BLOCKED. The sender never learns.
   6. The sender retransmits the same too-big packet. Forever.

   RESULT: the connection CONNECTS and then HANGS.

Learn this fingerprint. It is one of the most common "impossible" bugs:

"SSH connects then freezes." "Small web pages load, big ones hang." "git clone stalls at a few KB." "It works on the office Wi-Fi but not over the VPN."

All four are usually MTU.

How to diagnose it in 30 seconds

ping can send a specific size with "don't fragment" set:

# Linux
ping -M do -s 1472 -c 2 example.com     # 1472 + 8 (ICMP) + 20 (IP) = 1500

# macOS
ping -D -s 1472 -c 2 example.com
  • Works? Path MTU is at least 1500. Good.
  • "Message too long" / "Frag needed"? Something on the path is smaller.

Now binary search for the real limit:

for s in 1472 1452 1422 1372 1272; do
  echo -n "payload $s: "
  ping -M do -s $s -c 1 -W 2 example.com >/dev/null 2>&1 && echo OK || echo TOO BIG
done

The largest size that works, plus 28, is your path MTU.

Or just use:

tracepath example.com        # Linux -- reports the MTU per hop, no root needed

How to fix it

Fix When
Allow ICMP type 3 through your firewalls Always. This is the correct fix.
MSS clamping on the router/VPN — rewrite the MSS in the handshake so both ends agree to smaller packets Standard on VPN gateways
Lower the interface MTU on the affected machine (ip link set eth0 mtu 1400) Quick local workaround
Enable tcp_mtu_probing (Linux) so TCP finds the limit itself without ICMP Good defensive setting

Practice (25 min)

A. Find your own path MTU to three destinations: your router, a nearby site, a distant site. Record them.

B. Break it on purpose (safe, and instructive):

# Linux -- lower your MTU, see what happens, then put it back
ip link show eth0 | grep mtu           # note the current value
sudo ip link set eth0 mtu 1200
curl -s -o /dev/null -w '%{http_code}\n' https://example.com
sudo ip link set eth0 mtu 1500         # PUT IT BACK

Small MTU still works (just less efficiently) because PMTUD is functioning. The breakage comes from a mismatch plus blocked ICMP.

C. See fragmentation:

sudo tcpdump -i any -n 'ip[6:2] & 0x3fff != 0'   # captures only fragments
# in another terminal:
ping -s 4000 -c 1 192.168.1.1      # a 4000-byte ping must be fragmented

D. If you use a VPN, run the MTU test with the VPN on and off. Compare.

Common confusions

  • "Bigger MTU is always better." Only if every device on the path agrees. Jumbo frames (9000) are great inside a data centre and useless across the internet, where something will be 1500.
  • "MTU vs MSS?" MTU is the whole packet including headers; MSS is just the data. MSS = MTU − 40 for normal TCP over IPv4.
  • "Why doesn't this fix itself?" It does — via PMTUD — unless someone blocks the ICMP that makes PMTUD work.

Check yourself

  1. What's the standard Ethernet MTU, and what's the resulting TCP MSS?
  2. What does a router do with a too-big packet that has the DF bit set?
  3. Describe the symptoms of a PMTU black hole.
  4. Why do VPNs so often cause MTU problems?

Further reading

  • Article (excellent): "Path MTU Discovery" — Cloudflare blog, and their post on "Fixing an old hack" about MTU issues in the wild.
  • Video: "MTU and MSS Explained" — Practical Networking.
  • Blog: search "MTU black hole troubleshooting" — many good war stories; reading a few builds pattern recognition fast.

Generated from real-life-guide-v1.md · 8 Oct 2026 16:36