WireGuard handshake but no internet is the second half of a two-part failure, and it is the half with a working tunnel. wg show prints a recent latest handshake, both transfer counters move, and every page still times out. That combination rules out keys, endpoints and firewalls, because none of those would have let the handshake through in the first place.
If there is no handshake at all, you are on the wrong page. Read WireGuard handshake did not complete after 5 seconds instead, because the four causes below all assume an established tunnel.
Confirm the tunnel really is up
Run wg show and read it properly before assuming anything.
sudo wg showThree signals matter. latest handshake should be recent, usually within the last two minutes. The transfer line should show both received and sent bytes rather than one of them. Small movement on both is enough, because a handshake and a few retries produce traffic on their own.
That last point is where people get stuck. Counters that move a little are not proof that data is flowing. The poster in angristan/wireguard-install discussion #210 describes the state precisely:
"I can connect from the client because I can see the handshake with wg show, i can see the download/upload counters moves a little. But Internet goes in timeout when I try to surf from the client"
A few kilobytes on each side means the tunnel is negotiating and nothing more. Megabytes on each side means real traffic is passing, and your problem is narrower than this page. Watch the counters change across two runs a few seconds apart rather than reading one snapshot.
Note what this state already rules out. The handshake is authenticated, so both public keys are correct. The handshake round-tripped, so the endpoint address is right and UDP is reaching the peer in both directions. Nothing on this page involves keys, ports or firewalls, because all three are already proven working.
That is why the check order below is short. Four causes remain once the handshake succeeds, and they sit at three different layers: packet size, routing, and name resolution, with the server side underneath all of them.
WireGuard handshake but no internet: four causes
Four faults produce a live tunnel that carries nothing useful. Work down them in this order, because each check is cheaper than the one below it.
MTU is too high for the path
WireGuard adds its own headers, so the payload that fits inside the tunnel is smaller than the payload that fits outside it. When the configured MTU exceeds what the path can carry, small packets succeed and large ones vanish, which is why a ping works and a web page hangs halfway. Set MTU = 1380 in the [Interface] section as a test, and MTU = 1280 if that is not low enough.
The full discovery procedure, and why 1420 is the usual starting value, is in finding your optimal WireGuard MTU.
AllowedIPs does not route what you think it routes
AllowedIPs is doing two jobs at once and only one of them is obvious. On the client it decides which destinations are routed into the tunnel. On the peer it decides which source addresses are accepted from you. A value that is too narrow produces a working tunnel that carries almost nothing.
Read what is actually loaded rather than what the file says.
sudo wg show wg0 allowed-ips0.0.0.0/0 covers the entire IPv4 address space and nothing else. It does not cover IPv6. On a dual-stack network your client keeps a global IPv6 address and a default IPv6 route on the physical interface. IPv6-capable sites resolve to an AAAA record and then leave outside the tunnel entirely. Some of them hang, some load unprotected, and the difference is invisible from a browser.
The narrower case is worth checking too. A config written for split tunnelling may list only the peer's own subnet, such as AllowedIPs = 10.8.0.0/24. That routes traffic to the peer and nothing else, which produces a tunnel that reaches the gateway and no destination beyond it. Compare the loaded value against what you expected rather than against what the file says, because wg-quick normalises and merges ranges as it applies them.
Add the IPv6 half if you want a full tunnel.
AllowedIPs = 0.0.0.0/0, ::/0Confirm which interface the kernel actually chooses afterwards.
ip route get 1.1.1.1
ip route get 2606:4700:4700::1111Both should name wg0. If the IPv4 lookup names wg0 and the IPv6 lookup names your physical interface, AllowedIPs is the cause. MTU and AllowedIPs covers what the field controls in more depth.
DNS is not being applied
The DNS = line lives in the [Interface] section and it is not implemented by WireGuard itself. wg-quick reads it and shells out to a resolver helper. When that helper is absent, the line is parsed, nothing happens, and no error reaches you.
Separate the two layers with one pair of commands.
ping -c 3 1.1.1.1
ping -c 3 example.comAn address that replies while a name fails is a resolver fault and not a routing fault. Confirm what the resolver stack believes.
resolvectl status wg0
resolvectl query example.comIf the wg0 link section lists no DNS server, the DNS = line never applied. The wg-quick man page hands that value to resolvconf, so the line does nothing unless a resolvconf implementation is installed, and distributions differ in whether they ship one. Setting a public resolver by hand works and sends every lookup outside the tunnel, which is the leak described in DNS settings for the tunnel.
Check your own config for a DNS = line before chasing the resolver stack. A config without one was never going to set a resolver, and the fault is in the file rather than in the system.
The server is not forwarding or not masquerading
The last cause is not yours. A peer that completes handshakes and forwards nothing is missing IP forwarding, a NAT rule, or both. On a self-hosted server that is net.ipv4.ip_forward and a MASQUERADE rule. On a commercial service you have no access to either.
What you can do is prove the fault is on that side. Ping the tunnel gateway, which is normally the first address in your tunnel subnet.
ping -c 3 10.8.0.1A reply from the gateway with nothing reachable beyond it puts the fault squarely on the server. Substitute the address your own config uses.
Test a second server endpoint from the same client before you conclude anything. One working endpoint and one dead endpoint is the cleanest evidence you can gather from outside the server. If every endpoint behaves the same way, the fault is more likely to be on your side after all, and the three causes above deserve another pass.
There is a second reason a working tunnel carries nothing, and it is easy to mistake for a server fault. A local firewall on your own machine can accept the handshake and then drop forwarded traffic on the tunnel interface. Check that the interface is not caught by an existing rule set before you blame the far end.
The test that tells you which one
Run these while the tunnel is up. The first result that does not match the expected one names your cause.
| Test | Result | Cause | Section |
|---|---|---|---|
ping -c 3 1.1.1.1 | Replies | Routing into the tunnel works | Continue to the name test |
ping -c 3 1.1.1.1 | Timeout | AllowedIPs too narrow, or the server is not forwarding | AllowedIPs, then the server section |
ping -c 3 example.com | Name or service not known | The DNS = line never applied | DNS is not being applied |
ping -M do -s 1372 1.1.1.1 | Replies | Path carries a 1400-byte packet | MTU is not your cause |
ping -M do -s 1372 1.1.1.1 | Frag needed and DF set or silence | Path MTU is below the configured value | MTU is too high for the path |
curl -o /dev/null https://example.com/large-file | Stalls after a few kilobytes | Large packets are being dropped | MTU is too high for the path |
ip route get 2606:4700:4700::1111 | Names the physical interface | IPv6 is leaving outside the tunnel | AllowedIPs |
ping -c 3 10.8.0.1 | Replies, nothing beyond it does | Server-side forwarding or NAT | The server is not forwarding |
Two notes on the MTU rows. ping -M do -s 1372 sends a 1400-byte packet once headers are added, which is the size that separates a healthy path from a constrained one. Use ping -f -l 1372 on Windows and ping -D -s 1372 on macOS instead.
Read whatever rejection your own platform prints rather than matching the string above. The wording differs between implementations, and any message naming fragmentation means the same thing.
Platform notes
The four causes are identical everywhere. What differs is which of them you hit and what you can see.
Windows
The resolver-helper gap described above is a wg-quick problem, and wg-quick is not what runs here, so that cause does not apply. Check the adapter in ipconfig /all and confirm the tunnel adapter holds the resolver you expect. If the tunnel has "Block untunneled traffic" enabled, read that setting as a control over what can leave the machine rather than over what routes inside the tunnel.
macOS utun
macOS names the interface utun0 upward and renumbers it between connections, so never hardcode the name. Resolver state is not in a file you can read, so use scutil --dns and confirm the tunnel resolver sits above your physical interface. MTU is the most common cause on macOS laptops that roam between networks. Run ifconfig utun0 to read the value actually in force.
Linux wg0
wg0 is the conventional name and it comes from your file name rather than from WireGuard. Confirm it with ip -br link before writing any rule that names it. The resolver-helper gap described above is a Linux-specific problem and it is the most common cause on this platform. Everything else behaves as the generic sections describe.
iOS and Android
The official apps apply the DNS = line themselves through the platform tunnel interface, so the Linux resolver-helper failure cannot occur here. That removes one of the four causes and leaves the other three, which is why mobile reports cluster on MTU and on routing.
Two settings inside the apps are worth checking before anything else, because both rewrite AllowedIPs without saying so. The Android app offers an exclude-private-IPs control that expands 0.0.0.0/0 into a long list of public ranges with the private ranges removed. That is a convenience for reaching your LAN, and it is also a silent change to what routes. Read the field afterwards rather than assuming it still says what you typed. On both platforms an on-demand or always-on setting can bring the interface up before the network is ready. That produces a tunnel that looks active and carries nothing until you toggle it.
MTU is the other mobile-specific cause, and it moves under you. A value that works on wifi can be too large on cellular, because carrier paths frequently run smaller. Set MTU = 1280 as a test in whichever edit view your app provides, then read the field back afterwards to confirm it took.
If it worked yesterday and not today
A tunnel that worked and then stopped points at something that moved rather than something misconfigured. Four candidates cover almost all of it.
- Your client roamed to a new network and its path MTU shrank, so large packets now drop where they previously passed.
- The server rotated its endpoint address or its keys, and your file still names the old ones.
- A client or system update replaced the resolver integration, so the
DNS =line stopped applying. - An IPv6 default route appeared on a network that previously had none, exposing a missing
::/0.
Notice that three of the four are the same causes as above, arriving from a different direction. Nothing new happens when a working tunnel breaks. What changes is the network underneath it.
Take a fresh config file first. It is faster than diagnosing and it eliminates key rotation and endpoint changes in one step. Re-importing the config covers where the file goes on each platform, and installing WireGuard covers the client itself.
If the handshake has also stopped succeeding, the failure has moved to the other side of the boundary. Read WireGuard handshake did not complete after 5 seconds for that case.
Quick answers
- How do I know whether this is an MTU problem?
- Send a packet that cannot be fragmented and see whether it survives. Run ping -M do -s 1372 1.1.1.1 through the tunnel on Linux, ping -f -l 1372 on Windows. A reply means the path carries 1400 bytes and MTU is not your cause. Silence or a fragmentation error means it is, and lowering MTU to 1380 will usually confirm it immediately.
- Is my VPN leaking?
- Run ip route get 1.1.1.1 and ip route get 2606:4700:4700::1111. Both should name your tunnel interface. If the IPv6 lookup names a physical interface, IPv6 traffic is bypassing the tunnel entirely. Check DNS separately, because a resolver reached outside the tunnel leaks every domain you visit even when routing is correct.
- Why does DNS stop working when the tunnel comes up?
- Because the tunnel replaces your resolver with one that is only reachable inside it. If routing to that resolver is broken, or the DNS = line never applied, you end up with a working tunnel and no name resolution. Test with ping 1.1.1.1 against ping example.com. The first succeeding while the second fails is the signature.
- Does AllowedIPs 0.0.0.0/0 send everything through the tunnel?
- It sends all IPv4 traffic through the tunnel and no IPv6 traffic at all. On a dual-stack network that leaves a live IPv6 default route on your physical interface, so IPv6-capable destinations bypass the tunnel. Use AllowedIPs = 0.0.0.0/0, ::/0 for a genuine full tunnel, and confirm with ip route get against an IPv6 address.
- Why does it work on wifi but not on mobile data?
- Carrier networks frequently run a smaller path MTU than home broadband, so a value that works on wifi drops large packets on cellular. Try MTU = 1280, which is the IPv6 minimum and survives almost any path. Carrier-grade NAT is the other candidate, and it shows up as handshakes that expire during idle time rather than as traffic that will not pass. Work through connection troubleshooting for the other symptoms in this cluster. For the fields behind these four causes, see MTU and AllowedIPs.