WireGuard "Handshake did not complete after 5 seconds": 8 causes

"Handshake for peer did not complete after 5 seconds, retrying"? What the log line means, how to check with wg show, then 8 ordered fixes: firewall, port, NAT, keys, MTU.

By Eric GerardFounder · VPNSmith - Self-host VPN & GDPR VPS specialist

Updated 21 min read21 min

Affiliate links: we may earn a commission, at no extra cost to you. Method

Generate my configRead the guide

fig. 01 / self-host-vpnencrypted tunnelUDP 51820DEVICE10.8.0.2SERVER10.8.0.1INTERNETNATAllowedIPs = 0.0.0.0/0, ::/0ListenPort = 51820 (server)
fig. 01 / self-host-vpn

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.

fig. 01 / self-host-vpnencrypted tunnelUDP 51820DEVICE10.8.0.2SERVER10.8.0.1INTERNETNATAllowedIPs = 0.0.0.0/0, ::/0ListenPort = 51820 (server)
fig. 01 / self-host-vpn

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.

WireGuard port checker

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 peer is a different case: that side has no Endpoint = 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 received means 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.

A 3D mesh of connected cube-shaped nodes linked by thin lines on a black background
A 3D mesh of connected cube-shaped nodes linked by thin lines on a black background

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:

  • 0 means 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 +%s to 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.

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.co on 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 AllowedIPs must 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/0 routes 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:

  1. wg show on both sides - is there any latest handshake, any received bytes?
  2. tcpdump udp port 51820 on the server while the client connects - do packets arrive?
  3. If no packets arrive → firewall / port / Endpoint (Causes 1-2).
  4. If packets arrive but no reply gets back → server-side AllowedIPs, NAT, return route (Causes 3-4).
  5. If handshake completes but no internet → ip_forward + masquerade (Cause 5).
  6. 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

Sources and references:


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

Price checked 17 Oct 2026, 24-month term

Frequently asked questions

What does 'handshake did not complete' actually mean in WireGuard?
It means your peer sent the first handshake packet but never received a valid reply within the timeout. WireGuard is silent by design: it sends nothing until there's traffic to carry, so a missing handshake almost always means the initiation packet didn't reach the server, or the server's reply didn't reach you. The cause is network-level (firewall, port, NAT, routing) far more often than a key problem.
How do I see the last handshake time?
Run `sudo wg show` on either side. Look at the `latest handshake` line under the peer. If it says nothing, or a time that keeps growing past ~2-3 minutes, the tunnel is down. A healthy peer shows a handshake within the last 0-120 seconds and non-zero `transfer` counters.
My keys look right but still no handshake - what next?
Keys are rarely the issue if `wg show` lists the peer at all. Check in this order: (1) is UDP 51820 open in the cloud provider firewall AND the OS firewall, (2) does the client's Endpoint point to the server's real public IP and correct port, (3) is `net.ipv4.ip_forward=1` set on the server, (4) is the server clock within ~2 minutes of real time. A wrong public/private key pair shows as a handshake that never completes even though packets flow, so swap keys last.
Why does the handshake work for two minutes then stop?
Classic NAT timeout. If the client is behind NAT (home router, mobile) and idle, the router drops the UDP mapping, so the server can no longer reach the client. Fix: add `PersistentKeepalive = 25` to the peer block on the side that sits behind NAT. It sends a tiny packet every 25 seconds to keep the path open.
What does `wg show latest-handshakes` output actually mean?
One line per peer: the peer's base64 public key, a tab, then the time of its last handshake as seconds since the Unix epoch. A `0` means that peer has never completed a handshake since the interface came up, which is exactly the failure this page covers. The number is an absolute timestamp, not an age, so subtract it from `date +%s` to get seconds elapsed. Note that `wg show all latest-handshakes` prepends the interface name as an extra first column.
Can MTU cause a failed handshake?
Rarely the handshake itself (handshake packets are small), but it routinely breaks the tunnel right after: the handshake completes, then large packets silently drop. If ping works but web pages hang, lower the client MTU to 1380 (or 1280 on PPPoE/mobile). Test with `ping -M do -s 1372 <server-tunnel-ip>`.