How it works
VPN stalls on mobile data: MTU, fragmentation and PMTUD black holes (2026)
There is a failure mode that looks like a slow server and is not one. The tunnel comes up in under a second. Ping through it is fine. Small pages load. Then something with a real payload - a git clone, an image-heavy site, a file download - hangs at zero bytes per second and stays there. On Wi-Fi the same connection is fine; on mobile data it is not. That pattern is usually about packet size: MTU, fragmentation, and path MTU discovery being silently dropped somewhere between you and the server.
The symptom: small requests work, large transfers stall
The failure has a recognisable shape. Anything that fits in a small packet goes through. Anything that fills one does not.
- A TLS handshake completes and the page never renders. ClientHello and ServerHello are small; the first full-size record is not.
- SSH connects and prompts, then freezes the moment you run something that prints a screenful of output.
- A download starts, transfers a few kilobytes, and sits at zero. No error, no reset - the connection stays open and empty.
- The same file fails at the same place on a second attempt, and on a third, from the same network.
Where the headroom goes
MTU is the largest IP packet a link will carry. Ethernet uses 1500 bytes. A PPPoE line gives 1492. Any link that carries IPv6 must support at least 1280. The path MTU is the smallest of all the MTUs along the route, and it is the number that actually matters.
When a packet is larger than the path allows, one of three things happens: it gets fragmented, it gets rejected with an ICMP message telling the sender the correct size, or it disappears. The third case produces the symptom above.
A packet-mode tunnel wraps your packet in another one, and the wrapper takes bytes out of the payload budget. WireGuard over IPv4 costs 60 bytes: 20 for the outer IP header, 8 for UDP, 32 for its own header and authentication tag. Over IPv6 the outer header is 40 instead of 20, so the cost is 80. That is why the WireGuard default interface MTU is 1420 rather than 1440 - wg-quick subtracts the larger figure so the same value is safe on either family.
The arithmetic only holds if the path really is 1500. Drop the true path MTU to 1400 and a 1420-byte inner packet becomes a 1480- or 1500-byte outer packet that nothing will carry. The tunnel keeps handshaking happily - a WireGuard handshake packet is under 200 bytes - and the data plane dies.
Not every tunnel has an MTU problem
This matters before you start changing numbers. Two different things get called a VPN, and only one of them carries your packets verbatim.
- Packet-mode tunnels - WireGuard, OpenVPN in tun mode, IPsec. Your IP packet travels inside another IP packet unchanged. The inner MTU has to be small enough that the wrapped result fits the path. This is where MTU tuning belongs.
- Proxy-mode clients - Xray with VLESS, sing-box, and most mobile clients that run a tun interface in front of a proxy. The tun stack terminates TCP in userspace and opens its own connection to the server, so your inner segments are re-packetized on the way out and the tun MTU does not set the size of anything on the wire. Large defaults there are deliberate, and lowering the value mostly buys you more trips through the userspace stack.
The exception inside proxy mode is UDP, which is relayed as whole datagrams rather than as a byte stream. An application that emits a datagram too large for the outer transport to carry in one piece can still fail, and what happens then depends on the proxy protocol - some frame and reassemble oversized datagrams themselves, some drop them. QUIC is the common case and handles it on its own, which is why this is rarely the thing that breaks.
For a TCP transport such as VLESS Reality the only path MTU that exists is the one on the outer connection, and the kernel owns it: both ends cap their segments during the handshake, and Linux can fall back to packetization-layer probing (net.ipv4.tcp_mtu_probing) when the path turns out smaller. That is more robust than a fixed tunnel MTU, but it is not immunity - with probing left at 0, a mid-path reduction plus filtered ICMP produces the same black hole one layer out. If a VLESS session stalls on mobile, look there, at plain loss on the cellular path, and at a middlebox in between.
Path MTU discovery and the black hole
IPv4 packets that set the Don't Fragment bit - which includes essentially all TCP, and UDP from most modern stacks - cannot be split by a router in transit. A router that cannot forward one is supposed to drop it and reply with ICMP type 3, code 4 (fragmentation needed), carrying the next-hop MTU. The sender caches that number and sends smaller packets. That is path MTU discovery, and it works well when the ICMP message arrives.
It frequently does not. Someone along the way filters ICMP wholesale - a hosting firewall, a carrier's edge, or the tunnel operator's own iptables rule that drops all ICMP because it looked like hardening. The oversized packet is gone and the sender is never told why, so it retransmits the same oversized packet indefinitely. This is a PMTUD black hole: not an error, just silence.
ICMPv6 type 2, Packet Too Big. Filtering that message hardens nothing; it breaks IPv6 in a way that is hard to diagnose from the client side. If you run the server, allow it explicitly.Why mobile data makes it worse
Cellular networks add layers a wired path does not have. Traffic is encapsulated between the base station and the core network, then handed through carrier-grade NAT, then often through a transparent proxy or a traffic-shaping middlebox. Each layer can lower the effective MTU, and the value is not advertised to your device.
- Fragments are treated badly. CGNAT and firewall implementations commonly drop IPv4 fragments outright, or fail to reassemble them under load, because a fragment after the first carries no port numbers and cannot be matched to a session.
- The path MTU can change mid-session. Handover between cells, or a switch between IPv4 and IPv6 connectivity, can move it with no signal to either endpoint.
- Some cellular networks advertise an IPv6 link MTU below 1500. The standard permits anything down to 1280, so a config written for an Ethernet path can be wrong by a wide margin.
- Loss is real as well. A network can have both problems at once, which is why the diagnosis below is worth doing rather than guessing.
Telling a size problem from congestion
Both feel like a slow VPN. They behave nothing alike under inspection.
- Timing. Congestion tracks the clock - worse in the evening, better early in the morning. A size problem is identical at every hour.
- Latency under load. Congestion and bufferbloat push round-trip time up as the queue fills. With an MTU black hole, ping stays flat while throughput sits at zero, because the packets that would build a queue never arrive.
- Shape of the failure. Congestion gives you slow-but-moving transfers and variable rates. A size problem gives a hard stop, usually a few kilobytes in, once the sender leaves slow start and begins filling segments.
- Direction. A size problem often affects one direction only, so an upload completes while a download hangs, or the reverse.
- Reproducibility. Congestion is statistical. A size problem is deterministic: the same file, the same host, the same byte offset.
Measuring the real path MTU
Do not copy a value from a forum post. Measure, then subtract your encapsulation overhead. The method is a search with the Don't Fragment bit set.
- 1Turn the tunnel off and measure the bare path to your server's IP address first. You need to know whether the limit is upstream of the tunnel or created by it.
- 2Send a probe with fragmentation forbidden. Linux:
ping -M do -s 1472 <host>. macOS:ping -D -s 1472 <host>. Windows:ping -f -l 1472 <host>. - 3Step down on failure and back up on success, halving the remaining interval each time - 1472, 1372, 1422, and so on - until you find the largest payload that still gets a reply.
- 4Add 28 to that payload: 20 bytes of IPv4 header plus 8 of ICMP. A 1472-byte payload that succeeds means a path MTU of 1500.
- 5On Linux, cross-check with
tracepath <host>, which reports the discovered PMTU directly and shows roughly where it drops. - 6Repeat the probe through a packet-mode tunnel against a host on the far side. If the inner result is smaller than the configured tunnel MTU, the tunnel is not the binding constraint and the path outside it is.
What to change, on the client and on the server
- Set the tunnel MTU from the measurement, not from a default: measured path MTU minus 60 for WireGuard over IPv4, minus 80 over IPv6. If the link moves too much to measure reliably, work back from the IPv6 floor rather than guessing. No link carrying IPv6 may be under 1280, so a 1280-byte outer packet is the conservative target - that is an inner MTU of 1200 over IPv6, 1220 over IPv4. Setting the tunnel interface itself to 1280 is a weaker guarantee than it looks, because the packet on the wire is then 1360.
- Clamp TCP MSS for forwarded traffic. On a Linux gateway,
iptables -t mangle -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmturewrites the advertised segment size during the handshake, so the far end never builds an oversized packet. This covers TCP only; UDP still needs a correct MTU. - Turn on packetization-layer probing.
net.ipv4.tcp_mtu_probing=1lets TCP search for a size that gets through once it suspects a black hole, instead of retransmitting the same segment forever. It costs nothing and covers the case where the filtered ICMP is on somebody else's network and you cannot fix it. - Stop dropping the ICMP you depend on. Allow
ICMP type 3 code 4on IPv4 andICMPv6 type 2on IPv6, inbound and outbound. Blanket ICMP drop rules are a frequent self-inflicted cause of this failure. - Try a QUIC-based transport. QUIC sets the Don't Fragment bit, never leans on router fragmentation, and searches for a workable datagram size itself instead of trusting ICMP to come back. Where a fixed-MTU UDP tunnel such as WireGuard stalls, a QUIC-based one like Hysteria2 can adapt - see VLESS Reality vs Hysteria2 for how the two transports differ.
- Test IPv4 and IPv6 separately. Plenty of tunnels are healthy over one family and blackholed over the other, and a dual-stack client hides that behind what look like random stalls.
The cheapest diagnostic is often not a measurement but a swap: run the same route over a different transport and see whether the stall follows. If a fixed-MTU UDP tunnel freezes on a carrier and a TCP or QUIC one does not, you have located the limit without touching a single config value.
What a smaller MTU will not fix
Lowering the MTU is not free and it is not a general remedy. Every packet carries the same header cost over a smaller payload, so you spend more overhead per byte and more CPU per megabyte. Set it far below what the path supports and you have made a working link measurably worse.
- Steady but low throughput with rising latency under load is congestion or a rate limit. MTU changes nothing there.
- A tunnel that never completes its handshake is rarely an MTU issue on WireGuard, where handshake packets are under 200 bytes. It can be one on QUIC, which needs the path to carry a 1200-byte UDP payload before anything is established.
- Packet loss spread evenly across all packet sizes is loss, not a size threshold.
- Slow name resolution, or a page that takes a long time to start and then loads normally, is a resolver or routing question. One site that connects and then hangs is the opposite - that is the classic shape of a black hole on that site's own path, and it is usually not yours to fix.
Worth being clear about the boundary as well: all of this is transport behaviour. Tuning MTU changes how reliably your bytes move, and nothing else. A VPN replaces your network address and encrypts the hop to the server - it does not make you anonymous, and no packet-size setting changes that. That side is covered in does a VPN make you anonymous.
What MTU should I set for a VPN on mobile data?
Measure the path first and subtract your tunnel's overhead - 60 bytes for WireGuard over IPv4, 80 over IPv6. If the link moves too much to measure reliably, size the tunnel so the packet on the wire stays at or under 1280 bytes, the minimum any IPv6-capable link must support: that means an inner MTU of about 1200. Setting the interface itself to 1280 still puts a 1360-byte packet on the wire, which is not the guarantee people assume it is.
Why does the tunnel work on Wi-Fi but stall on mobile data?
Cellular paths add encapsulation between the radio and the core network, then carrier-grade NAT, and often a middlebox on top. The effective MTU ends up lower than the 1500 your config assumed, and IPv4 fragments are frequently dropped by NAT devices that cannot match a later fragment to a session. Settings that fit an Ethernet path do not fit that one.
Does lowering the MTU reduce speed?
Slightly. Headers stay the same size while the payload shrinks, so the overhead ratio rises and the device processes more packets for the same amount of data. That cost is small next to a connection that transfers nothing, but it is a reason to set the value from a measurement rather than dropping it to the minimum by reflex.
Is Hysteria2 immune to MTU problems?
No, but it fails differently. QUIC sets the Don't Fragment bit, never relies on router fragmentation, and probes for a workable datagram size itself instead of waiting for an ICMP message that may be filtered. It does need the path to carry a 1200-byte UDP payload - about 1228 bytes on IPv4 - so on a path narrower than that it fails to connect rather than stalling halfway.
How do I know it is MTU and not just a congested cell?
Check whether the failure depends on size or on load. If small requests always succeed and large transfers always stop at roughly the same point, at any hour, with ping staying flat, it is a size problem. If everything is uniformly slower in the evening and round-trip time climbs while you transfer, it is congestion.