Affiliate disclosure — This post contains Contabo affiliate links. If you grab a VPS through them, we earn a commission at no extra cost to you. Every command and config below is documented from official WireGuard sources and written to be reproducible on your own VPS.
You typed wg show, you see your peer, and under it sits the line that ruins your evening: latest handshake is empty, or wg-quick warns handshake did not complete. Your tunnel is up on paper but no traffic flows. The good news: WireGuard fails for a short, finite list of reasons, and they fall in a predictable order. This guide walks that order from most-common to least-common, so you stop guessing and fix it in minutes.
WireGuard is silent by design — it sends nothing until there is data to carry, and it never logs a friendly "connection refused". That silence is what makes a failed handshake feel mysterious. Almost always it is a network-reachability problem (firewall, port, NAT, routing), not a cryptography problem. Work the list top to bottom and you will catch it.
First, read the actual state with wg show
Before changing anything, look at what WireGuard already tells you. On both ends:
sudo wg show
You are reading three things:
latest handshake— empty or older than ~2 minutes = the tunnel is down. Healthy = within the last 120 seconds.transfer—0 B receivedmeans your packets never came back. Sent-but-not-received is the signature of a one-way firewall or NAT block.endpoint— on the server, this should show the client's current public IP:port once a packet arrives. If it never populates, the client's initiation never reached you.
Keep this terminal open. After each fix below, re-run wg show and watch the latest handshake line. That is your single source of truth — not the connection icon, not the app.
Cause 1 — The UDP port is blocked (the #1 culprit)
WireGuard speaks UDP on a single port — 51820 by default. If that port is filtered anywhere on the path, the initiation packet dies and you get exactly the symptom you have. For the default, how to change it and how to open it correctly, see what port WireGuard uses and how to open it. There are usually two firewalls to check, and people forget the first one:
Cloud provider firewall (the one most often missed). Contabo, Hetzner, OVH, AWS — they all have a network-level firewall or security group in front of your VPS. A rule allowing UDP 51820 from anywhere must exist there, separate from the OS firewall. On most Contabo VPS images there is no provider firewall by default (good), but if you enabled one, add the UDP rule.
OS firewall on the server. Check and allow:
# ufw (Debian/Ubuntu)
sudo ufw allow 51820/udp
sudo ufw reload
# firewalld (Rocky/Alma/Fedora)
sudo firewall-cmd --add-port=51820/udp --permanent
sudo firewall-cmd --reload
Quick proof the port is reachable from the outside — from your client machine:
# sends a UDP probe; "open|filtered" with no reply is inconclusive,
# but "open" or a returned packet confirms reachability
nc -u -z -v YOUR_SERVER_IP 51820
A cleaner test: run sudo tcpdump -n -i eth0 udp port 51820 on the server, then connect from the client. If you see incoming packets, the port is open and you move to the next cause. If you see nothing, the packet is being dropped before it reaches WireGuard — stay on this cause.
Cause 2 — Wrong Endpoint on the client
The client's [Peer] block needs the server's real public IP (or DNS name) and the exact ListenPort:
[Peer]
PublicKey = SERVER_PUBLIC_KEY
Endpoint = 203.0.113.10:51820
AllowedIPs = 0.0.0.0/0
Common mistakes that produce a dead handshake:
- Endpoint points to the server's private LAN IP (
10.x/192.168.x) instead of the public one. - A typo in the port, or the server actually listens on a different
ListenPort. - The server's public IP changed (some VPS reinstalls reassign it). Confirm with
curl -4 ifconfig.coon the server, then compare to the client's Endpoint.
The server peer block has no Endpoint line (the server learns it when the client's packet arrives) — adding one there is a frequent copy-paste error.
Cause 3 — AllowedIPs are wrong
AllowedIPs is a cryptographic routing table, not a permission list, and getting it wrong silently breaks return traffic:
- On the server, the peer's
AllowedIPsmust list that client's tunnel address only, e.g.10.66.66.2/32. If two peers share an overlapping range, the server sends replies to the wrong one and the handshake "completes" then dies. - On the client,
AllowedIPs = 0.0.0.0/0routes everything through the tunnel (full VPN). If you want split tunnelling, list only the subnets you actually route — see our split-tunnel routing guide.
A handshake that briefly succeeds and then stalls is the classic signature of overlapping server-side AllowedIPs. Make every peer's range unique.
Cause 4 — NAT timeout (works for 2 minutes, then dies)
If your client is behind NAT (home router, 4G/5G, hotel Wi-Fi) and the link goes idle, the router drops the UDP mapping. The server then has no path back to the client and the handshake silently expires. The fix lives on the peer that sits behind NAT:
[Peer]
PublicKey = SERVER_PUBLIC_KEY
Endpoint = 203.0.113.10:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25
PersistentKeepalive = 25 sends a tiny packet every 25 seconds, holding the NAT mapping open. Twenty-five seconds is below every common NAT timeout. Add it on roadwarrior clients and on any peer behind a restrictive router. The server-side peer usually does not need it.
Cause 5 — IP forwarding is off on the server
If the handshake completes but you cannot reach the internet through the tunnel, the server is not forwarding packets between the WireGuard interface and eth0. Enable it:
echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl --system
Confirm: sysctl net.ipv4.ip_forward must return 1. Pair it with the masquerade rule in your PostUp (the ready-to-use config templates ship these lines correctly):
PostUp = iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE
Cause 6 — Clock skew
WireGuard's handshake uses timestamps to block replay attacks. If the server clock is off by more than a couple of minutes — common on fresh VPS images or VMs that paused — handshakes are rejected as stale. Fix the clock:
sudo timedatectl set-ntp true
timedatectl status # System clock synchronized: yes
This one is sneaky because everything else looks perfect. If you have triple-checked the port, keys and routing and still get nothing, check the clock before despairing.
Cause 7 — Keys are mismatched (check this last)
People reach for keys first; check them last, because a key error is rarer than the six causes above and produces the same symptom. The rule is simple:
- The server's
[Peer] PublicKey= the public key derived from the client's PrivateKey. - The client's
[Peer] PublicKey= the public key derived from the server's PrivateKey.
Regenerate and re-derive to be certain:
# on each machine, derive the public key from its own private key
echo "PRIVATE_KEY_OF_THIS_HOST" | wg pubkey
Compare that output to the PublicKey the other side has for you. A single swapped or truncated key breaks the handshake completely. While you are at it, a PresharedKey (if used) must be identical on both peers — a mismatch there also kills the handshake.
Cause 8 — MTU breaks the tunnel right after the handshake
Strictly, MTU rarely blocks the handshake (those packets are small). But it routinely breaks things the moment real traffic flows: handshake completes, ping works, then web pages hang forever. That is fragmentation. Lower the client MTU:
[Interface]
MTU = 1380
Find the right value with a do-not-fragment ping to the server's tunnel IP, shrinking until it passes:
ping -M do -s 1372 10.66.66.1
1420 suits most fibre links; 1380 is a safe default; PPPoE and some mobile carriers need 1280. Our config templates guide explains MTU tuning per link type.
The 60-second diagnostic order
When a handshake fails, run this exact sequence and stop at the first thing that's wrong:
wg showon both sides — is there anylatest handshake, anyreceivedbytes?tcpdump udp port 51820on the server while the client connects — do packets arrive?- If no packets arrive → firewall / port / Endpoint (Causes 1–2).
- If packets arrive but no reply gets back → server-side
AllowedIPs, NAT, return route (Causes 3–4). - If handshake completes but no internet →
ip_forward+ masquerade (Cause 5). - If everything looks right and still nothing → clock skew, then keys, then MTU (Causes 6–8).
This order maps to how often each cause actually appears in real self-host setups — work it top to bottom and you will not waste time.
Build it right from the start
Most failed handshakes come from hand-stitching a config out of three contradictory tutorials. Starting from a known-good base on a clean VPS removes 90% of the surprises. A Contabo VPS S at €4.99/month gives you a public IPv4, full root, and no provider firewall to fight — exactly the conditions where WireGuard "just works". Follow the step-by-step Contabo + WireGuard setup and paste a vetted config from the templates guide, and the handshake completes on the first try.
Going further
- Self-host VPN on Contabo: full WireGuard guide 2026
- Ready-to-use WireGuard config templates 2026
- WireGuard DNS leaks: detect, diagnose, eliminate
- WireGuard kill-switch on Linux: iptables + systemd
- Split-tunnel VPN with routing tables
- Self-hosted VPN glossary — AllowedIPs, MTU, NAT explained
Sources and references:
- WireGuard whitepaper — Jason A. Donenfeld
- WireGuard quickstart and wg-quick docs
- wg(8) and wg-quick(8) man pages
- Arch Wiki — WireGuard troubleshooting
Published 2026-06-23. Diagnostic order validated on Debian 12 and Ubuntu 24.04 servers with a Contabo VPS S, against WireGuard clients on Linux, Windows 11, macOS and Android. Your link conditions may differ — always confirm with wg show and tcpdump before considering a fix final.
Reminder: running WireGuard and self-hosting a VPN is legal in the EU, US, Canada and most democratic countries. VPNSmith publishes this content for educational purposes.
★ Nuremberg GDPR datacenter · ✓ Dedicated IPv4 included · 200+ Mbps guaranteed
Self-host your VPN on your own VPS → ContaboFull root access · public IPv4 · pick your region→
