Every page that answers the WireGuard optimal MTU question hands you a number. Usually 1420, sometimes 1380, sometimes 1280. A number is the wrong shape of answer, because the correct value depends on a link you have and the author does not. What you actually need is the arithmetic behind those numbers and a way to measure your own path. Both are below, and both are short. Derive the overhead first, then measure, then set the value once.
What MTU actually breaks
An MTU that is too large does not break the tunnel. The handshake completes, wg show reports a recent handshake, and the counters move. Small things work perfectly. Large things stall.
That combination is what makes MTU hard to recognise:
- SSH sessions connect and then freeze the moment a command prints a screenful of output.
- Web pages load their HTML and hang on a large image.
- File uploads start, transfer a few hundred kilobytes, and stop.
- One direction is fine while the other direction is unusable.
The mechanism is the same in every case. Your tunnel accepts a packet up to its configured MTU, wraps it, and hands the result to the underlying link. If the wrapped packet is larger than the link will carry, it is dropped. Small packets are under the limit and pass, so the connection looks alive the whole time.
Why 1420 is a starting point and not an answer
Build the number rather than copying it. Start with the link.
A standard Ethernet link carries a 1500-byte IP packet. That is the outer budget, and everything WireGuard adds comes out of it.
WireGuard puts your inner packet inside a UDP datagram. Four fixed costs sit between the inner packet and the link.
| Layer | IPv4 outer path | IPv6 outer path | Where the number comes from |
|---|---|---|---|
| Outer IP header | 20 | 40 | An IPv4 header without options is 20 bytes. The fixed IPv6 header is 40 bytes. |
| UDP header | 8 | 8 | Source port, destination port, length and checksum, at 2 bytes each. |
| WireGuard data message header | 16 | 16 | 1 byte message type, 3 bytes reserved, 4 bytes receiver index, 8 bytes counter. |
| Poly1305 authentication tag | 16 | 16 | The tag the ChaCha20-Poly1305 AEAD appends to every encrypted payload. |
| Total overhead | 60 | 80 |
Add the columns. 20 + 8 + 16 + 16 = 60 on IPv4, and 40 + 8 + 16 + 16 = 80 on IPv6.
Subtract each from the link:
IPv4 outer path: 1500 - 60 = 1440
IPv6 outer path: 1500 - 80 = 1420There is 1420. It is not a tuned value and it is not a benchmark result. It is the IPv6 column, and it is the default because it is the conservative one. A tunnel set to 1420 fits whichever address family your Endpoint resolves to, which is why every article repeats it.
wg-quick reaches it the same way. Its manual page says only that the MTU is automatically determined from the endpoint addresses or the system default route. The Linux implementation is more specific than that. It takes the smallest MTU among the routes to its endpoints, falls back to the default route, falls back again to 1500, and subtracts 80. The 80 is a constant. It does not drop to 60 for an IPv4 endpoint. An IPv4 tunnel left on the default therefore gets the IPv6 answer and gives up 20 bytes of payload.
The 16-byte term nobody counts
WireGuard pads the inner packet with zeros before it encrypts, so that the plaintext length is a multiple of 16 bytes. The protocol specification is explicit about it: the encapsulated packet is followed by "zero padding in order to make the length a multiple of 16".
That padding is part of the outer packet, so it belongs in the sum. The full expression for an inner packet of L bytes is:
outer IP header + 8 + 16 + roundup16(L) + 16Run it at L = 1420 on an IPv6 outer path. 1420 divided by 16 is 88.75, so the padded length is 89 x 16 = 1424.
40 + 8 + 16 + 1424 + 16 = 15041504 is four bytes over a 1500-byte link. Apply the same arithmetic in reverse to find the value that does fit. The largest multiple of 16 at or below 1420 is 1408:
40 + 8 + 16 + 1408 + 16 = 1488The IPv4 column is tidier. 1440 is 90 x 16 exactly, so it needs no padding at all:
20 + 8 + 16 + 1440 + 16 = 1500Treat that as the honest status of 1420. It is a sound rounding, it is correct for almost all traffic, and it is four bytes optimistic for a maximum-size packet on an IPv6 endpoint.
Be careful about what that does and does not tell you. The arithmetic is derived, and the runtime consequence is not. Whether those four bytes are fragmented, dropped or absorbed depends on the implementation you are running. Measure your own path with the method below rather than reasoning about the last four bytes.
Finding the WireGuard optimal MTU by binary search
None of the numbers above are yours until you measure the link. Your path may be 1500, or 1492, or 1460, or something a carrier chose. Measure it with ping and the do-not-fragment flag.
Bring the tunnel down before you start. You are measuring the path to the endpoint, not the path through it.
The ping command, per platform
Note that the DF flag has a different letter on each platform. That is the single most common reason people copy a command from one operating system and run it uselessly on another.
Linux
ping -M do -s 1472 -c 3 vpn.example.com-M do selects the do-not-fragment path MTU discovery strategy. The iputils manual describes it as setting the DF flag while remaining subject to the kernel's own path MTU checks. An oversized packet is therefore rejected rather than sent. -s is the number of data bytes to send.
macOS
ping -D -s 1472 -c 3 vpn.example.com-D sets the do-not-fragment bit. BSD ping also sweeps a range, which saves the manual search:
ping -g 1400 -G 1472 -h 8 -D vpn.example.comThe macOS ping manual page marks -G as required for a sweep. It restricts only flood ping, preload and intervals under two milliseconds to the superuser, so a sweep runs as your ordinary user.
Windows
ping -f -l 1472 -n 3 vpn.example.com-f sets do not fragment. -l is the buffer size in bytes.
Start every one of them at 1472 for a reason. The ICMP header is 8 bytes and the IPv4 header is 20, so 1472 + 8 + 20 = 1500. A 1472-byte payload is exactly the largest that fits a standard Ethernet path.
Substitute 48 for 28 on an IPv6 endpoint, because the IPv6 header is 40 bytes and the ICMPv6 header is 8. Start at 1452, since 1452 + 8 + 40 = 1500. Microsoft documents -f as available on IPv4 only, and IPv6 does not need it, because routers never fragment an IPv6 packet in transit. Ignore any instruction to reach for a separate ping6 binary. The iputils manual states that ping6 stopped existing as of version s20150815 and was merged into ping, so on current Linux the name is a symlink at most.
Reading the result
Expect three outcomes, of which only two are useful.
A reply. The payload fits. Increase it.
A fragmentation-needed error. The payload is too large for some hop. The wording differs between ping implementations, and between versions of the same one, so do not match a string from a guide. Treat any message naming fragmentation, or naming the do-not-fragment flag, as the too-large result. Some of those messages also name the hop and print the MTU it will accept. When yours does, read that number and stop searching, because the number is the answer.
A timeout. This one is ambiguous and it is where most people go wrong. Plenty of hosts drop ICMP entirely, and plenty of networks drop the fragmentation-needed message that path MTU discovery depends on. If every size times out, including small ones, ICMP is filtered and this method cannot measure that path. Fall back to the table further down.
Converting the payload size to an MTU value
Work the search first, then the arithmetic.
- Set a low bound of 1200 and a high bound of 1472.
- Test the midpoint with the command for your platform.
- Move the low bound up on a reply, and the high bound down on a fragmentation error.
- Repeat until the bounds meet, which takes about eight tests.
Call the largest payload that replied P. Then:
1. path MTU = P + 28 (20-byte IPv4 header + 8-byte ICMP header)
2. tunnel MTU = path MTU - 60 if your Endpoint resolves to an IPv4 address
tunnel MTU = path MTU - 80 if your Endpoint resolves to an IPv6 address
3. optionally round down to the nearest multiple of 16Check the method against three worked examples before trusting it on your own link.
A clean Ethernet path, IPv4 endpoint. P = 1472. Path MTU is 1472 + 28 = 1500. Tunnel MTU is 1500 - 60 = 1440. 1440 is already 90 x 16, so nothing to round. Answer: 1440.
A DS-Lite access line, IPv6 endpoint. P = 1432. Path MTU is 1432 + 28 = 1460, which is 1500 minus the 40-byte IPv6 header that DS-Lite wraps your IPv4 traffic in. Tunnel MTU is 1460 - 80 = 1380. Rounded down to a multiple of 16 that becomes 1376. Answer: 1380, or 1376 if you want the padding to fit.
A PPPoE line, IPv4 endpoint. P = 1464. Path MTU is 1464 + 28 = 1492, which is 1500 minus the 8 bytes PPPoE takes. Tunnel MTU is 1492 - 60 = 1432, rounding down to 1424. Answer: 1432.
Notice what the second example produced. 1380 is not folklore. It is what a DS-Lite line arithmetically yields, which is why so many people on those lines independently arrive at the same value.
Common values and where they come from
Use this when ICMP is filtered and you cannot measure. Every row states its arithmetic, and the two rows that have none say so.
| MTU | Arithmetic | The path it fits |
|---|---|---|
| 1440 | 1500 - 60 | Plain Ethernet, Endpoint resolving to an IPv4 address |
| 1432 | 1492 - 60 | PPPoE line, IPv4 endpoint |
| 1420 | 1500 - 80 | Plain Ethernet, IPv6 endpoint. The conservative default, correct either way |
| 1412 | 1492 - 80 | PPPoE line, IPv6 endpoint |
| 1408 | largest multiple of 16 at or below 1420 | Exact fit on an IPv6 endpoint once the 16-byte padding is counted |
| 1400 | none | A round number widely reported to work. Empirical, not derived |
| 1380 | 1460 - 80 | DS-Lite or any path carrying one extra 40-byte encapsulation |
| 1280 | none | The minimum link MTU every IPv6 path is required to carry. A floor, not an optimum |
Spell out the caveat on the two rows with no arithmetic. 1400 is the value that appears in most forum threads, and it works because it is comfortably under every common limit rather than because it fits one. 1280 is a floor. It is the smallest MTU any IPv6 link is permitted to have, so a tunnel set to 1280 passes almost anywhere. Expect to pay for that in more packets and more per-packet overhead for the same data.
For the wider set of tunnel settings this interacts with, see WireGuard MTU and tunnel settings.
Setting it and confirming it helped
Put the MTU line in the [Interface] section, not in [Peer]. If you do not have a config open yet, start with installing WireGuard.
[Interface]
PrivateKey = <your private key>
Address = 10.7.0.2/32
DNS = 10.7.0.1
MTU = 1420
[Peer]
PublicKey = <server public key>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0, ::/0Apply it by restarting the interface, not by reloading. On Linux run wg-quick down wg0 then wg-quick up wg0. In the Windows and macOS clients, deactivate the tunnel and activate it again. Confirm the interface took the value with ip link show wg0 on Linux. On Windows and macOS, read the MTU back from the operating system rather than from the app. An app can show you what you typed rather than what the interface holds. Then repeat the transfer test below, which is the check that actually settles it.
For where to edit the file on each platform, see editing your config.
Then measure properly, because a single speed-test run proves nothing.
- Record a baseline before you change anything, by timing the same large transfer three times.
- Change the MTU and restart the interface.
- Repeat the identical transfer three times.
- Compare medians rather than best runs, and test upload and download separately.
Use a large transfer rather than a ping for the confirmation. MTU problems only appear on full-size packets, so a latency test will look identical before and after. Test both directions as well, because the classic MTU signature is one direction working and the other stalling.
Check one thing in your own file before you edit it. If the [Interface] section already carries an MTU line, change the value on that line rather than adding a second one. A config with two MTU lines behaves according to whichever the client reads last, and that is not a property you want to discover during a transfer.
What a PulseVPN config gives you to start from
A PulseVPN WireGuard config ships with no MTU = line at all. That is the useful fact here, and it has two consequences.
The first is that your client derives the value rather than reading it. On Linux wg-quick applies the rule described above, taking the route MTU to the endpoint and subtracting 80. An IPv4 endpoint therefore lands on 1420 rather than on the 1440 the arithmetic allows.
The second is that you add a line rather than editing one. Open the [Interface] section, put your measured value on its own line, and restart the interface. There is no existing value to overwrite and no duplicate to create.
The rest of the file is already populated. A PulseVPN config carries DNS = 1.1.1.1, AllowedIPs = 0.0.0.0/0, ::/0 and PersistentKeepalive = 25, so a full tunnel, a resolver and a NAT keepalive are all set for you. The MTU is the one field this page asks you to supply.
When MTU is not the problem
MTU is the first thing to check and it is not the only thing. Four other causes produce a slow tunnel and none of them respond to an MTU change.
- Distance to the server. A single stream is bounded by round-trip time and window size. Changing MTU does not change latency.
- Congestion on your access link. A saturated uplink is slow with and without a tunnel. Compare against the same test with the tunnel down.
- CPU on your own device. Small routers, older phones and low-end virtual machines hit an encryption ceiling well below their line rate.
- Capacity at the far end. The server is a shared machine with a shared uplink, and its limit is not visible from your side.
Rule each of them out with the tunnel down, then with it up, on the same test. For the wider symptom index, see connection troubleshooting. For how the protocols differ before you assume WireGuard is the constraint, see how the two protocols differ.
Quick answers
- Can MTU really change throughput that much
- Third-party reports say yes, where MTU is the binding constraint. User y0nei reported this on the widely cited WireGuard MTU gist: "I was getting ~35 Mbits/sec inside of my tunnel with having a MTU of 1500 both on client and server...after testing your values...im getting ~240 Mbits/sec" Treat that as one person on one link rather than as a benchmark. Measure your own before and after.
- Why does a lower MTU feel faster
- Because a wrongly sized packet is not slow, it is dropped. TCP then retransmits, backs off, and retries at a lower rate, so the connection stalls rather than degrades. Lowering the MTU stops the drops, and throughput returns to whatever the link supports. The same gist commenter pirmanji described the result as "my VPN has become workable and feels quite fast".
- Does a VPN make internet slower
- Usually a little, and rarely by much. Encryption costs CPU, the extra headers cost payload space, and routing through another city costs latency. A well-configured tunnel on a modern device gives up single-digit percentages of throughput. If yours is losing far more than that, the cause is usually MTU, distance or a saturated link rather than the protocol.
- Should I set MTU on the client, the server, or both
- MTU is a per-interface setting, so the client value controls only what your client emits. Setting it on the client fixes stalls in the direction you send. If the server side is also oversized, the return direction can still stall, and only the operator can change that. Set your client value from your own measurement and treat the far side as fixed.
- Is WireGuard slower than OpenVPN
- Sometimes, and MTU is the usual reason. WireGuard has less per-packet overhead than OpenVPN, so it should be faster on equal terms. An OpenVPN profile carrying mssfix clamps TCP segment size, which papers over a path MTU problem that a bare WireGuard config exposes instead. The OpenVPN manual gives the mssfix default as 1492 mtu, so that clamp is usually applied whether anyone chose it or not. Fix the MTU before concluding the protocol is at fault. For the settings this page sits inside, see WireGuard MTU and tunnel settings. For everything else that stalls a tunnel, see connection troubleshooting.