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.
Short on time? Two causes cover most failed handshakes: a UDP port that never reaches the server (closed firewall, missing forward, or CGNAT on your home line), then, once the tunnel is up, an MTU set too high. The WireGuard port checker tells you whether you sit behind CGNAT and writes the forwarding and firewall rules for your system; the MTU calculator gives the value to set once the handshake works.
Not sure which one you have? Our free WireGuard port checker compares your router's WAN address with your public IP: it tells you in under a minute whether you are behind CGNAT (no port forward can work) or whether a forward plus a firewall rule will do.
Behind CGNAT? No port forward or firewall rule on your side can open a path to a server at home. The usual way out is to run the WireGuard server on a VPS with its own public IPv4, such as a HostPapa Unmanaged VPS Lite (affiliate link, 2.95 $ a month on a 3-year term, then the regular rate) or a Contabo Cloud VPS (affiliate link), from 5.50 € a month on a 24-month term as read on 17 October 2026. If you would rather have a US host with a 30-day money-back guarantee, Ultahost VPS Basic is 5.52 $ a month on a 24-month term (1 GB RAM, dedicated IPv4, read on 21 October 2026), enough for a WireGuard server for a few devices. Choosing a host? Compare the best VPS for a VPN and Contabo vs Hetzner vs OVH on the price actually billed. HostPapa's Lite plan is the smallest one here: 1 core, 1 GB RAM, 25 GB NVMe and 4 TB transfer, unmanaged (root SSH on Ubuntu or Debian, no control panel, you install WireGuard yourself), enough for a WireGuard server for a few devices, 30-day money-back guarantee, read on 28 October 2026.
Paste your wg show output, get a verdict per peer
Run sudo wg show on either end and paste the whole output. It is read in your browser, nothing is sent or stored.
wg show prints (hidden) in place of the private key, so its output is safe to paste.
The exact log line: "Handshake for peer 1 did not complete after 5 seconds"
If you searched the error word for word, this is the message you are looking at. The Linux kernel module prints it (once debug logging is on), and wireguard-go, the userspace implementation behind the Windows, macOS and Android apps, prints a near-identical version:
wireguard: wg0: Handshake for peer 1 (203.0.113.10:51820) did not complete after 5 seconds, retrying (try 2)
wireguard: wg0: Handshake for peer 1 (203.0.113.10:51820) did not complete after 20 attempts, giving up
How to read it:
- "after 5 seconds" is not a setting you can tune. It is WireGuard's fixed retry timeout (
REKEY_TIMEOUT, 5 s in the source). Your peer sent a handshake initiation and got no valid response within that window. - "retrying (try 2)" means it is still trying. WireGuard keeps resending for about 90 seconds in total.
- "did not complete after 20 attempts, giving up" is the end of that loop. The peer goes quiet until new traffic tries to cross the tunnel, which starts a fresh round.
Failed to send handshake initiation: no known endpoint for peeris a different case: that side has noEndpoint =for the peer, so it cannot initiate at all and can only wait to be contacted. Normal on a server; a config error on a client.
On Linux the kernel module logs nothing by default. To see these lines:
echo module wireguard +p | sudo tee /sys/kernel/debug/dynamic_debug/control
sudo dmesg -w | grep wireguard
The message only proves one thing: the initiation left and nothing valid came back. It does not say why. The causes below, in order, are the why.
Check both files together: server wg0.conf and client .conf
Most failed handshakes are two files that do not match. Paste both; the keys are compared in your browser and never sent or stored.
WireGuard port checkerRebuild both files with matching keys: WireGuard config generator
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.
wg show latest-handshakes: what the man page specifies
If you came here looking for the official documentation of this subcommand, this section reproduces what the wg(8) man page defines - the accepted syntax, the exact output format, and the meaning of each field - and then shows what the values look like in practice. The authoritative source is the wg(8) man page itself; everything below is consistent with it.
Plain wg show prints a human-friendly latest handshake: 1 minute, 12 seconds ago. Good for eyeballing, useless for scripting. The wg(8) manual lets you append one attribute to narrow the output:
show { <interface> | all | interfaces } [public-key | private-key | listen-port |
fwmark | peers | preshared-keys | endpoints | allowed-ips |
latest-handshakes | persistent-keepalive | transfer | dump]
So the reference form is:
sudo wg show wg0 latest-handshakes
It prints one line per peer: the peer's base64 public key, a tab, then the last handshake as seconds since the Unix epoch. That is literally what the tool does - in wireguard-tools, src/show.c reads printf("%s\t%llu\n", key(peer->public_key), peer->last_handshake_time.tv_sec);:
xTIBd0Kx3Hs9tPmVvQ4ZlLxYcE8nRr2A6WKsPqUvT1s= 1750684173
9QzrN7bLyF0mXaHcJdSoEuIgYkTvW3pB5nRfZ1AhQ0M= 0
Two things to know about that number:
0means this peer has never completed a handshake since the interface came up. Not a stale timestamp - an absent one. That is the state you are debugging on this page.- It is an absolute epoch time, not an age. Subtract it from
date +%sto get seconds since the last handshake:
now=$(date +%s)
sudo wg show wg0 latest-handshakes | while read -r peer ts; do
if [ "$ts" -eq 0 ]; then
echo "$peer never"
else
echo "$peer $((now - ts))s ago"
fi
done
Two details that bite when you script this. wg show all latest-handshakes prepends the interface name as a first tab-separated column, so your read needs a third variable. And wg show wg0 dump gives you the same timestamp alongside endpoint, allowed-ips and the transfer counters in one tab-separated line per peer - that is the form to parse if you are building a real monitor rather than a one-liner.
Anything past ~180 seconds on a peer that should be active means the tunnel is down. That is the threshold to alert on, and the rest of this page is the ordered list of why it went down.
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.
Nothing arrives and the server runs at home? Check for CGNAT
If the "server" is a Raspberry Pi, a NAS or a PC on your home connection, an empty tcpdump often has nothing to do with your firewall. Many ISPs (most mobile and 4G/5G boxes, a growing number of fibre plans) put customers behind CGNAT: your router shares one public IPv4 with other subscribers, so no port forward can ever bring an incoming UDP packet to your machine. Compare the WAN address shown in your router with what curl -4 ifconfig.me returns. If they differ, or the router shows an address in 100.64.0.0/10, you are behind CGNAT and the handshake cannot complete as configured.
You then have three ways out: ask your ISP for a public IPv4 (some give it free, some charge, many refuse), use IPv6 end to end if both sides have it, or put the WireGuard server on a small VPS with its own public IPv4 and, if you still need to reach the home machine, connect it to the VPS as a client. The CGNAT guide walks the reverse-tunnel setup, and our free WireGuard config generator writes the server and client files with matching keys.
★ Nuremberg GDPR datacenter · ✓ Dedicated IPv4 included · 200 Mbit/s port
Get a public IPv4 for your WireGuard server → Contabo VPSCloud VPS 4 from €5.50/month (24-month term) · Public IPv4 included · No provider firewall enabled by default→Not sure a port forward can work at all on your connection? Our free WireGuard port checker compares your router's WAN address with your public IP, flags CGNAT, writes the forwarding and firewall rules for your setup, and gives the tcpdump test that proves packets reach the server.
Works at home, fails on hotel, office or campus Wi-Fi?
Then the server is fine and the network you are sitting on drops UDP, or fingerprints WireGuard and blocks it. The tcpdump test above shows nothing only while you are on that network. If you run the server, move ListenPort (and the client Endpoint) to UDP 443, the port HTTP/3 already uses, which gets through most port-based filters; our WireGuard port guide covers the change. If you just need a working VPN on that network and do not run a server, a VPN app with an obfuscation mode wraps the tunnel for you: Proton VPN has Stealth, on every plan including Free, and NordVPN has NordWhisper, which makes VPN traffic resemble regular web traffic (affiliate links). Against deep packet inspection, see the anti-DPI guide.
Paid plan, Black Friday 2026: Proton's partner offer, checked on 28 October 2026, is VPN Plus at $2.49/month over 2 years ($59.76 for the first 24 months, then $83.88 per year), with a 30-day money-back guarantee and 10 devices. The order page shows the price for your country, and Proton does not publish an end date.
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.
Need to exclude your LAN or a single address from a full tunnel? AllowedIPs has no "except" syntax, so our free AllowedIPs calculator turns "everything except X" into the minimal list of IPv4 and IPv6 ranges to paste.
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. What the preshared key actually protects, and how to add one without dropping a live tunnel, is in the WireGuard preshared key guide.
To rule keys out in one step, our free WireGuard config generator creates a fresh key pair for each side in your browser and writes each public key into the other side's [Peer] block, along with the same PresharedKey on both ends.
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. If the tunnel connects but stalls on anything large, the dedicated WireGuard MTU fix walkthrough shows how to measure the exact value with a do-not-fragment ping and set it per link.
To get the value instead of guessing, the free WireGuard MTU calculator takes the largest payload your do-not-fragment ping passed (or just your link type) and writes the MTU = line for your config.
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 follows the packet, from reachability to routing to the less obvious causes - work it top to bottom and you will not waste time.
Want this order on one printable page? The troubleshooting checklist in our paid WireGuard Self-Hosting Kit (9 USD) extends it to 12 causes, each with the command that confirms it and the fix, plus a table for reading tcpdump output. The steps above stay free and complete for the 8 causes covered here.
Not ready to buy? The free WireGuard Cheat Sheet (5-page PDF, direct download, no sign-up) holds the four one-line server checks to run first: the age of the last handshake per peer, whether UDP reaches the server at all (tcpdump), whether forwarding and masquerading are on, and the largest packet that passes without fragmentation.
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 most of the surprises. A Contabo Cloud VPS 4 at €5.50/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 you start from lines that are known to fit together.
If you would rather rebuild both ends from scratch, our free WireGuard config generator creates the keys in your browser and writes a matching server config and client configs, so the public keys, AllowedIPs and Endpoint line up on the first try.
Your VPN hides your IP. It does not hide your browser.
A correctly configured WireGuard tunnel replaces the address a site sees. It changes nothing about what your browser announces on its own: fonts, screen, time zone, graphics stack. That combination is often unique enough to follow you across sessions, tunnel or no tunnel.
Measure your own browser fingerprint, passive, no questions, no account, no email. It tells you how many browsers out of N look like yours, and which attribute makes you stand out.
Going further
- PersistentKeepalive: what the 25-second setting really does
- WireGuard multihop: chaining two self-hosted servers
- WireGuard behind CGNAT: why the handshake never arrives
- WireGuard on Windows: step-by-step setup
- WireGuard on iPhone and iPad: the full iOS setup
- 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
- PiVPN in 2026: the easiest way to self-host WireGuard or OpenVPN
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. Commands written for Debian 12 and Ubuntu 24.04 servers from the official WireGuard documentation; we have not run a test bench across clients. 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. VPNSmith publishes this content for educational purposes.
Keys are also worth auditing where they sit: a July 2026 CISA advisory covers a RouterOS flaw that lets a static WireGuard private key be read from the router itself. See why disabling the API does not undo it.
If the handshake fails because the server itself is the weak point (throttled network, oversold node), the provider matters more than the config. Our cheapest-VPS comparison sets out what each one bills for a usable WireGuard endpoint.
If the kernel log shows Required key not available rather than silence, the cause is narrower than anything on this list: the four causes of that error.
Affiliate link
★ Nuremberg GDPR datacenter · ✓ Dedicated IPv4 included · 200 Mbit/s port
Self-host your VPN on your own VPS → ContaboCloud VPS 4 · 5,50 €/mo · Card or PayPal · orders from outside the EU accepted→Price checked 17 Oct 2026, 24-month term


