VPNSmith
self-host-vpnINFO

WireGuard PostUp and PostDown iptables Rules: What Each Line Is For

Every WireGuard server config on the internet carries the same four iptables lines, copied without explanation. Here is what each one does, which of them you can drop, and the two mistakes that make a tunnel connect and then carry nothing.

By Eric Gerard · Founder · VPNSmith - Self-host VPN & GDPR VPS specialist4 min readPhoto via Pexels

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.

Nearly every WireGuard server config carries some version of this block, and almost nobody who pastes it can say what the third line does:

PostUp   = iptables -A FORWARD -i %i -j ACCEPT; iptables -A FORWARD -o %i -j ACCEPT; iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
PostDown = iptables -D FORWARD -i %i -j ACCEPT; iptables -D FORWARD -o %i -j ACCEPT; iptables -t nat -D POSTROUTING -o eth0 -j MASQUERADE

It is worth ten minutes, because these lines are where a working tunnel and a silent one part company.

What PostUp and PostDown actually are

They are not WireGuard features. They are shell commands that wg-quick runs after bringing the interface up and after tearing it down. WireGuard moves encrypted packets and does nothing else: no NAT, no filtering, no forwarding. Everything on top of that is a command you put here.

%i is expanded by wg-quick to the interface name, so the same config file works whether the interface is wg0 or wg1.

This also means PostDown matters more than it looks. Rules added with iptables -A accumulate. A tunnel restarted five times without matching PostDown lines leaves five copies of each rule, and the resulting chain is a mess to read six months later.

Line by line

iptables -A FORWARD -i %i -j ACCEPT accepts packets arriving on the tunnel that need to be forwarded somewhere else. This is traffic from a client heading for the internet.

iptables -A FORWARD -o %i -j ACCEPT accepts packets going out through the tunnel, which is the return path. Without it, replies come back to the server and stop there.

iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE rewrites the source address of outgoing packets to the server's own public address. Without this, a website receives a packet from 10.10.0.2, a private address, and its reply goes nowhere.

Note eth0 in that line. On many modern distributions the interface is not called eth0. Check with ip route get 1.1.1.1 and use whatever it names. A copied config with the wrong interface produces exactly the symptom people describe as "connects but no internet".

A small black five port TP-Link gigabit desktop switch on a wooden surface, a white cable in port one and a red cable in port five, their yellow and green link lights lit along the front edge
A small black five port TP-Link gigabit desktop switch on a wooden surface, a white cable in port one and a red cable in port five, their yellow and green link lights lit along the front edge

The kernel setting the rules cannot replace

None of this works while the kernel refuses to forward:

sysctl -w net.ipv4.ip_forward=1
echo "net.ipv4.ip_forward=1" >> /etc/sysctl.d/99-wireguard.conf

Add net.ipv6.conf.all.forwarding=1 if peers use IPv6. Writing it only with sysctl -w works until the next reboot, and then the tunnel comes up fine and carries nothing, which is a memorable way to spend an evening.

Two rules worth adding, and one worth deleting

Add an MSS clamp if clients report that some sites load and others hang:

PostUp = iptables -t mangle -A FORWARD -o %i -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu

This makes TCP negotiate a segment size that fits the tunnel, and it removes a whole class of complaints. It overlaps with the MTU question and does not replace it.

Restrict the masquerade to your tunnel subnet rather than everything leaving the box:

iptables -t nat -A POSTROUTING -s 10.10.0.0/24 -o eth0 -j MASQUERADE

On a single purpose VPS the difference is theoretical. On a machine doing other work it stops the rule applying to traffic that was never yours to translate.

Drop the FORWARD accept lines if your server is a pure peer to peer endpoint where each side only reaches the other. Nothing is being forwarded, so the rules do nothing except suggest a routing setup that is not there.

If you already use ufw or nftables

Mixing raw iptables commands with ufw works, because ufw is a front end to the same tables, but the result is a firewall whose state is described in two places. Pick one and stay there: either ufw owns the rules and you add yours with ufw route allow, or ufw is disabled and PostUp owns everything.

On a distribution using nftables natively, the iptables command is usually a compatibility shim. It works. It also means nft list ruleset shows your rules in a translated form that does not match what you wrote, which is worth knowing before you conclude something has gone wrong.

For a full server build with these rules in place from the start, see self-hosting WireGuard on a VPS, and for locking the tunnel down further, WireGuard kill switch with iptables and systemd.

★ 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

Frequently asked questions

What do PostUp and PostDown do in a WireGuard config?
They are shell commands run by wg-quick immediately after the interface comes up and immediately after it goes down. WireGuard itself does no firewalling or NAT, so anything beyond moving encrypted packets, forwarding and address translation included, has to be done by commands placed here.
Do I need iptables rules for WireGuard at all?
Only if the server routes traffic on behalf of its peers. A pure peer to peer tunnel where each side only talks to the other needs none. A server acting as an internet gateway for its clients needs forwarding and masquerade rules.
What is the difference between MASQUERADE and SNAT here?
MASQUERADE rewrites the source address to whatever the outgoing interface currently has, which is what you want when that address can change. SNAT hardcodes a fixed address and is marginally cheaper. On a VPS with a static IP either works; MASQUERADE survives an address change without editing anything.
Why does my WireGuard tunnel connect but no traffic passes?
The two usual causes are IP forwarding still disabled in the kernel, and a FORWARD chain whose policy is DROP without an accept rule for the wg interface. Both let the handshake succeed, because the handshake terminates on the server itself and never needs forwarding.