WireGuard config generator

Generate wg0.conf, client configs, keys and QR codes, entirely in your browser.

Last updated

In short: A WireGuard VPN needs one key pair per machine, a wg0.conf on the server with one [Peer] block per client, and a .conf per client that points at the server's public key and endpoint. Enter your endpoint, tunnel subnet and client names to get all of them, plus a QR code per client for the mobile app. Put wg0.conf in /etc/wireguard/ with mode 600, turn on IP forwarding and run sudo systemctl enable --now wg-quick@wg0.

Your input never leaves your browser. Everything is calculated on your device; nothing you type is sent to a server.

Keys are generated on your device and never leave this browser. They are made with the Web Crypto API (or a bundled X25519 library on older browsers) and nothing is uploaded. For a production server, generate the server's private key on the server itself and choose "Paste the server's public key" below, so that key never exists anywhere else.
Which traffic goes through the tunnel?
Server private key

On the server: sudo sh -c 'umask 077; wg genkey | tee /etc/wireguard/wg0.key | wg pubkey'

Firewall (firewalld 1.0 or newer)

Warning: with the trusted zone, every VPN client can reach every service on the server, so one lost phone exposes all of them.

Server setup commands

  1. Turn on IP forwarding, so the server routes client traffic:
    printf 'net.ipv4.ip_forward = 1\n' | sudo tee /etc/sysctl.d/99-wireguard.conf && sudo sysctl --system
  2. Open the UDP port and bind wg0 to its own wireguard zone, which accepts only the services you ticked. With a full tunnel or extra networks, the wireguard-out policy lets client traffic out through the internet-facing zone and masquerades (NATs) it; a tunnel-only split setup gets no forwarding at all. Clients cannot reach each other, because the zone's intra-zone forwarding stays off:
    sudo firewall-cmd --permanent --zone=public --add-port=51820/udp && sudo firewall-cmd --permanent --new-zone=wireguard && sudo firewall-cmd --permanent --zone=wireguard --add-interface=wg0 && sudo firewall-cmd --permanent --zone=wireguard --add-service=ssh && sudo firewall-cmd --permanent --new-policy=wireguard-out && sudo firewall-cmd --permanent --policy=wireguard-out --add-ingress-zone=wireguard && sudo firewall-cmd --permanent --policy=wireguard-out --add-egress-zone=public && sudo firewall-cmd --permanent --policy=wireguard-out --set-target=ACCEPT && sudo firewall-cmd --permanent --policy=wireguard-out --add-masquerade && sudo firewall-cmd --reload
  3. Install the config with mode 600 and start it now and on every boot:
    sudo install -m 600 wg0.conf /etc/wireguard/wg0.conf && sudo systemctl enable --now wg-quick@wg0
  4. Check the handshake with sudo wg show: each connected client shows a latest handshake time.

How does the WireGuard config generator work?

WireGuard is a VPN built into the Linux kernel. Every machine has a key pair: a private key that stays on the machine and a public key that identifies it to the others. Both are 32-byte X25519 keys written in base64, the same format wg genkey and wg pubkey produce. There are no user names, passwords or certificates.

A config file has one [Interface] section for the machine itself (its private key, its tunnel address and, on the server, the UDP ListenPort) and one [Peer] section for every machine it talks to. A peer is identified by its public key, and its AllowedIPs decide which addresses are routed to it and which source addresses it may use. The WireGuard whitepaper calls this cryptokey routing.

In a typical road-warrior setup, the server listens on a public UDP port (51820 by convention) and knows every client. Each client knows only the server: its public key, its Endpoint and which traffic to send through the tunnel. The client starts the handshake; the server learns the client's current address from it, which is why clients behind NAT need PersistentKeepalive to stay reachable.

This generator allocates addresses from your tunnel subnet (server first, then clients, never the network or broadcast address), creates all keys locally and writes the files in the format described in the wg(8) and wg-quick(8) man pages.

Worked examples

A laptop and a phone, all traffic through the tunnel

Input Endpoint vpn.example.com:51820, subnet 10.8.0.0/24, clients laptop and phone, full tunnel gives Server Address = 10.8.0.1/24; laptop 10.8.0.2/32; phone 10.8.0.3/32; client AllowedIPs = 0.0.0.0/0, ::/0

The server takes the first host address and each client gets the next free one as a /32. On the server, each [Peer] only accepts its own /32. On the clients, 0.0.0.0/0 and ::/0 send all IPv4 and IPv6 traffic through the tunnel, so the server must forward and masquerade it.

Reach the home network only (split tunnel)

Input Split tunnel, extra network 192.168.1.0/24 gives Client AllowedIPs = 10.8.0.0/24, 192.168.1.0/24

Only traffic for the tunnel subnet and your LAN goes through WireGuard; normal browsing stays on the local connection. It breaks when the network you are on uses the same range, for example a hotel that also hands out 192.168.1.x, so pick an uncommon LAN range if you can.

Dual-stack tunnel with IPv6

Input IPv4 subnet 10.8.0.0/24, IPv6 subnet fd42:42:42::/64, one client gives Server Address = 10.8.0.1/24, fd42:42:42::1/64; client Address = 10.8.0.2/32, fd42:42:42::2/128

fd00::/8 is the unique local range (RFC 4193), the IPv6 equivalent of 10.0.0.0/8. The forwarding command then also sets net.ipv6.conf.all.forwarding = 1. Check that your firewall also NATs IPv6 if clients should reach IPv6 sites through the server.

Frequently asked questions

Why does my WireGuard handshake never complete?

Almost always because the UDP packets never reach the server, or a public key is in the wrong place. WireGuard stays silent when it gets a packet it cannot authenticate, so a wrong key looks exactly like a closed port. Check in order: the router forwards UDP 51820 to the server; sudo firewall-cmd --zone=public --list-ports shows 51820/udp (use your internet-facing zone); the Endpoint resolves to your current public IP; the client's [Peer] PublicKey is the server's public key (not the client's own); the server's [Peer] has the client's public key; and the PresharedKey, if used, is identical on both sides. sudo wg show prints a 'latest handshake' line once it works.

What does AllowedIPs mean in WireGuard?

AllowedIPs is both a routing table and an access list for one peer. Outgoing packets to those addresses are encrypted to that peer, and incoming packets from that peer are dropped unless their source address is in the list; the WireGuard whitepaper calls this cryptokey routing. On the server, give each client its own /32 (and /128). On a client, 0.0.0.0/0, ::/0 means 'send everything to the server', and wg-quick adds the matching routes.

Do I need PersistentKeepalive in WireGuard?

Only on a peer behind NAT or a stateful firewall that must stay reachable, and 25 seconds is the interval the wg(8) man page suggests. WireGuard sends nothing when idle, so the NAT mapping on the client's router expires, often within 30 to 120 seconds, and the server can no longer reach the client until it sends again. Set it on the clients; the server usually does not need it. The cost is one small packet every 25 seconds, which matters a little for phone battery.

How do I stop DNS leaks with WireGuard?

Set DNS = in each client config and make sure that DNS server is reached through the tunnel. wg-quick on Linux applies it with resolvconf (or resolvectl on systemd-resolved systems), and the Android and iOS apps apply it directly. With a full tunnel, keep ::/0 in AllowedIPs so IPv6 queries cannot bypass it. With a split tunnel, a public resolver like 9.9.9.9 is outside AllowedIPs and is queried over your normal connection; use a resolver on the tunnel subnet, such as Unbound on 10.8.0.1, if that matters.

Is it safe to generate WireGuard keys in a browser?

Yes for testing and for client keys you move straight to the device, because the keys come from the browser's cryptographically secure generator and are never sent anywhere. For a production server it is better if the server's private key never exists outside the server: run sudo sh -c 'umask 077; wg genkey | tee /etc/wireguard/wg0.key | wg pubkey' there, choose 'Paste the server's public key', and the generated wg0.conf loads the key with PostUp = wg set %i private-key /etc/wireguard/wg0.key.

Which firewalld zone should the wg0 interface be in?

Give wg0 its own zone, not trusted, so a lost phone or leaked client config only exposes what you chose. This generator creates a zone called wireguard with firewall-cmd --new-zone, binds wg0 to it and allows only the services you tick, such as ssh or dns. For a full tunnel or LAN access it adds a policy (firewalld 1.0 or newer, see the firewalld.policies man page) from the wireguard zone to your internet-facing zone with target ACCEPT and masquerade, so clients can go out but the internet cannot come in. The trusted zone is still offered, but it lets every client reach every service on the server.