Why Your Website Hangs: Fixing MTU and MSS Issues on Linux

Networking tutorial - IT technology blog
Networking tutorial - IT technology blog

The Frustration of the Partial Loading Website

You just pushed your web app to a fresh Linux server. On your local 1Gbps fiber connection, it is lightning fast, and the logs are clean. But when a user connects through a corporate VPN or a remote branch office, the experience falls apart. Small pages load instantly, yet the main dashboard—filled with heavy JavaScript and high-res assets—spins indefinitely until the connection eventually times out.

I have seen teams spend entire weekends refactoring SQL queries or upgrading load balancer instances to solve this. They assume the code is slow, but the problem usually lies deeper in the network plumbing. If your site works for some but hangs for others, you are likely dealing with a packet size mismatch.

This behavior is a classic sign of a Maximum Transmission Unit (MTU) conflict. When a packet is too large for a router to handle, and that router can’t communicate the limit back to your server, the data simply vanishes. This is the dreaded “PMTUD Black Hole.” Here is how to diagnose and fix it.

The Basics: MTU vs. MSS

To fix the pipe, you need to understand how much water it can hold. Data travels across the internet in specific chunks.

MTU (Maximum Transmission Unit)

MTU is the largest packet size an interface can handle. Standard Ethernet uses an MTU of 1500 bytes. This total includes the IP header, the TCP header, and your actual data payload.

Problems arise when traffic hits a restricted path. For instance, WireGuard VPNs often use a 1420 MTU, while some PPPoE DSL connections top out at 1492. If your server sends a 1500-byte packet into a 1420-byte tunnel, the packet must be fragmented or dropped.

MSS (Maximum Segment Size)

MSS lives inside the TCP header. It tells the other side exactly how much actual data (the payload) can be sent in one segment. Unlike MTU, MSS does not count the headers.

The math is straightforward: MSS = MTU – 40 bytes (for standard IPv4). On a standard network, your MTU of 1500 yields an MSS of 1460. If you are using IPv6, the headers are larger, so your MSS will be lower.

Hunting the Bottleneck with Ping

Don’t guess—measure. You can use the ping command to find the exact MTU limit between your server and a client.

The “Do Not Fragment” Test

Run this from your local machine toward the server:

ping -s 1472 -M do [server_ip]
  • -s 1472: Sets the payload size. (1472 payload + 8 ICMP header + 20 IP header = 1500 bytes).
  • -M do: Forces the network to drop the packet if it requires fragmentation.

If the ping returns “Frag needed,” the path cannot handle 1500 bytes. Drop the -s value by 10 until it succeeds. If it finally passes at 1432, your maximum MTU is 1460 (1432 + 28 bytes of headers).

Two Ways to Fix Your Linux Server

Once you identify the limit, you have two options: change the hardware interface setting or force the TCP handshake to use a smaller size.

Option 1: Adjust the Interface MTU

This is the best approach if your server sits behind a specific tunnel like GRE or WireGuard. First, check your current settings:

ip link show eth0

To test a lower limit immediately, use:

sudo ip link set dev eth0 mtu 1460

To make this stick on Ubuntu or Debian, update your Netplan config (usually in /etc/netplan/):

network:
  version: 2
  ethernets:
    eth0:
      dhcp4: true
      mtu: 1460

Run sudo netplan apply to commit the changes.

Option 2: MSS Clamping (The Universal Workaround)

You cannot control the MTU of every router on the internet. MSS Clamping solves this by intercepting the initial TCP connection (the SYN packet). It replaces the client’s requested MSS with a value you specify, ensuring all subsequent packets stay small enough to pass.

Use iptables to automate this. This command calculates the best MSS based on the current path:

sudo iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

If you are working directly on a web server rather than a router, apply it to the OUTPUT chain instead:

sudo iptables -t mangle -A OUTPUT -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --set-mss 1360

A value of 1360 is generally safe for almost any VPN or encapsulated path.

Verifying the Results

After applying the fix, the “hanging” should stop immediately. You can watch the negotiation happen in real-time with tcpdump. Run this on your server while refreshing the site:

sudo tcpdump -i eth0 -n tcp[tcpflags] == tcp-syn

Check the mss value in the output. It should now match your new limit, confirming that the server and client have agreed to use smaller, safer packets.

Why Doesn’t This Work Automatically?

The internet has a built-in fix called Path MTU Discovery (PMTUD). When a router sees a packet that is too big, it is supposed to send an ICMP “Destination Unreachable” message back to the sender. This tells the sender to try a smaller size.

Unfortunately, many firewall admins block all ICMP traffic, thinking it prevents ping attacks. By doing so, they kill PMTUD. The router drops your packet but never tells your server why. Your server keeps waiting for an acknowledgment that never comes, and the user sees a spinning wheel.

The Bottom Line

Network hangs aren’t always about high latency or lack of RAM. Often, the pipes are just too small for the data you are trying to shove through them. By measuring the path MTU and using MSS clamping, you can ensure your Linux server stays responsive for every user, regardless of their connection type. Next time a site hangs for a specific user, stop looking at the code and start looking at the packet size.

Share: