WireGuard Handshake Timeout — Tunnel Not Establishing

Intermediate VPN & Routing

A WireGuard VPN tunnel fails to establish because the initial cryptographic handshake never completes. The client sends handshake initiation packets but receives no response from the server peer, resulting in a tunnel that shows `(none)` for the latest handshake in `wg show` and no ability to pass traffic. This is one of the most common WireGuard issues and usually has a network or configuration cause rather than a software bug.

Symptoms

  • `wg show` on the client displays `latest handshake: (none)` for the peer
  • Ping to the VPN gateway address (e.g., 10.8.0.1) fails with 100% packet loss
  • No traffic counters increment in `wg show` (transfer stays at 0 B received)
  • WireGuard logs show repeated 'Sending handshake initiation' without a corresponding response
  • Tunnel works from one network but not another (e.g., works on LAN, fails on mobile data)
  • Server-side `wg show` never lists a recent handshake from the expected client public key

Possible Root Causes

  • Firewall on the server (iptables, nftables, cloud Security Group) blocking inbound UDP on the WireGuard port
  • Incorrect public key in the peer configuration — keys are asymmetric and must cross-reference correctly
  • Server has a dynamic IP and the client's Endpoint is outdated; WireGuard has no built-in DNS re-resolution
  • NAT or stateful firewall between client and server dropping UDP sessions before the handshake completes
  • Server's WireGuard interface is not listening (service not running, config syntax error, or wrong port)

Diagnosis Steps

Step 1: Check WireGuard Status on Both Ends

# On the client
sudo wg show

# Expected output when tunnel is up:
# peer: <server-public-key>
#   endpoint: 203.0.113.1:51820
#   allowed ips: 0.0.0.0/0
#   latest handshake: 23 seconds ago
#   transfer: 1.23 MiB received, 456 KiB sent

# On the server (SSH in separately)
sudo wg show
# Check if client's public key appears and has a recent handshake

Step 2: Verify UDP Port Reachability

WireGuard uses UDP (default port 51820). TCP tools like telnet/curl won't work — use nc:

# From the client machine, test if the server's UDP port is open
nc -zvu 203.0.113.1 51820
# "Connection to 203.0.113.1 51820 port [udp/--] succeeded" = reachable

# Alternative: send a UDP packet and watch server-side tcpdump
# On server:
sudo tcpdump -n -i eth0 udp port 51820
# On client: attempt wg connection and observe if packets arrive

Step 3: Check Server Firewall

# On the server — list iptables/nftables rules
sudo iptables -L -n -v | grep 51820
sudo nftables list ruleset | grep 51820

# AWS / cloud: check Security Group rules in the console for UDP 51820

# ufw:
sudo ufw status verbose | grep 51820

Step 4: Validate Configuration Consistency

# Server /etc/wireguard/wg0.conf
sudo cat /etc/wireguard/wg0.conf

# Verify:
# 1. Server ListenPort matches what the client's Endpoint uses
# 2. Client's PublicKey in the server [Peer] block is correct
# 3. Server's PublicKey in the client [Peer] block is correct
# 4. AllowedIPs on the server peer entry includes the client's Address

# Decode and display a WireGuard public key from a private key:
wg pubkey < /etc/wireguard/server_private_key

Step 5: Test Bidirectional Connectivity

# From server, attempt to ping the client's VPN address
ping -c4 10.8.0.2

# If ping fails even after handshake, check AllowedIPs routing
ip route show table all | grep wg0

Solution

Fix 1: Open the Firewall Port

# iptables (persistent)
sudo iptables -A INPUT -p udp --dport 51820 -j ACCEPT
sudo iptables -A FORWARD -i wg0 -j ACCEPT
sudo iptables -A FORWARD -o wg0 -j ACCEPT
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo netfilter-persistent save

# ufw
sudo ufw allow 51820/udp

# AWS Security Group: add inbound rule — Type: Custom UDP, Port: 51820, Source: 0.0.0.0/0

Fix 2: Correct Public Keys

# Regenerate keys if unsure
wg genkey | tee /etc/wireguard/client_private.key | wg pubkey > /etc/wireguard/client_public.key
cat /etc/wireguard/client_public.key  # This goes in the server's [Peer] PublicKey

# Verify server public key
wg pubkey < /etc/wireguard/server_private.key  # This goes in the client's [Peer] PublicKey

Fix 3: Use a Hostname for Dynamic Server IPs

In the client config, use a hostname instead of a bare IP:

[Peer]
PublicKey = <server-public-key>
Endpoint = vpn.example.com:51820
AllowedIPs = 0.0.0.0/0
PersistentKeepalive = 25

WireGuard re-resolves the hostname on each handshake attempt. PersistentKeepalive = 25 also helps keep the tunnel alive through NAT.

Fix 4: Enable IP Forwarding on the Server

# Check current state
sysctl net.ipv4.ip_forward

# Enable (persistent)
echo "net.ipv4.ip_forward = 1" | sudo tee /etc/sysctl.d/99-wireguard.conf
sudo sysctl -p /etc/sysctl.d/99-wireguard.conf

# Restart WireGuard
sudo wg-quick down wg0 && sudo wg-quick up wg0

Prevention

  • Use a static public IP or a stable DDNS hostname for the WireGuard server endpoint to avoid stale IP issues
  • Set PersistentKeepalive = 25 on mobile or NAT-traversal clients to maintain the tunnel through stateful firewalls
  • Store configuration in a version-controlled secrets manager and validate key pairs with wg pubkey before deploying
  • Monitor the tunnel with a cron job that pings the server VPN IP and alerts if no response for 2 consecutive checks
  • Test connectivity from a separate network (mobile hotspot) immediately after setup to catch firewall issues early

Related Protocols

Related Terms

More in VPN & Routing