UFW has no forward command, but it can still do port forwarding. You add a small NAT section to one of its rule files, turn on IP forwarding and allow the routed traffic. This is how you send a public port on your VPS to a service behind a VPN, on a private network or in a lab at home. This guide builds a working example step by step and shows how to test it.
Before you start
- A Linux VPS running Ubuntu 22.04, Ubuntu 24.04 or Debian 12 with UFW active and SSH allowed. See Set up the UFW firewall on Ubuntu and Debian.
- A backend that the VPS can reach, for example a WireGuard client at
10.8.0.2running a web server on port 80. A tunnel is the usual setup; see Set up a WireGuard VPN server. - The name of the VPS's public interface. Find it with:
ip -br addrIt is often eth0 or ens3. The examples use eth0.
In this guide, traffic to 203.0.113.10:8080 (the VPS) is forwarded to 10.8.0.2:80 (the backend).
Warning: You will edit /etc/ufw/before.rules. A mistake there stops UFW from loading. Back it up first with sudo cp /etc/ufw/before.rules /etc/ufw/before.rules.bak, keep your SSH rule in place and keep the browser console in your VPS provider's control panel open as a way back in.
Turn on IP forwarding
Linux does not pass packets between interfaces until forwarding is on. UFW applies kernel settings from its own file, /etc/ufw/sysctl.conf, every time it starts. Open it:
sudo nano /etc/ufw/sysctl.confFind the commented line #net/ipv4/ip_forward=1 and remove the #:
net/ipv4/ip_forward=1If you forward IPv6 too, also uncomment net/ipv6/conf/default/forwarding=1 and net/ipv6/conf/all/forwarding=1.
Setting it here, rather than only in /etc/sysctl.conf, matters because UFW's file is applied when UFW starts and can override other settings. Enable IP forwarding on Linux explains the setting itself.
Decide how routed traffic is allowed
UFW's default forward policy lives in /etc/default/ufw:
grep DEFAULT_FORWARD_POLICY /etc/default/ufwIt is "DROP" by default. You have two choices:
- Keep DROP and add route rules (recommended). Only the forwards you name are allowed.
- Change it to "ACCEPT". Everything is forwarded. This is simpler but turns the server into an open router for any network it can reach.
This guide keeps DROP and adds a rule with ufw route, which manages the forward chain:
sudo ufw route allow proto tcp from any to 10.8.0.2 port 80 comment 'Forward 8080 to backend'Note the port is the backend port (80), because the address has already been rewritten by the time the forward rule is checked.
Add the nat table to before.rules
Open the file:
sudo nano /etc/ufw/before.rulesAt the very top, before the existing *filter line, add this block:
*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A PREROUTING -i eth0 -p tcp --dport 8080 -j DNAT --to-destination 10.8.0.2:80
-A POSTROUTING -d 10.8.0.2 -p tcp --dport 80 -j MASQUERADE
COMMITWhat it does:
- PREROUTING DNAT rewrites the destination of packets that arrive on
eth0for port 8080, sending them to10.8.0.2:80. - POSTROUTING MASQUERADE rewrites the source of those packets to the VPS's own address on the way out. The backend then sends replies back through the VPS, which is required when the backend's default route does not point at the VPS (normal for a WireGuard client or a home server).
The trade-off of masquerading: the backend sees every request as coming from the VPS, not from the real visitor. If the backend's default route already goes through the VPS, you can drop the MASQUERADE line and keep real client IPs.
To give VPN clients internet access through the VPS as well, add this line before COMMIT:
-A POSTROUTING -s 10.8.0.0/24 -o eth0 -j MASQUERADEThe *nat block must have its own COMMIT and must sit outside the *filter section.
Apply the changes
sudo ufw disable
sudo ufw enableThen confirm the kernel and NAT settings:
sysctl net.ipv4.ip_forward
sudo iptables -t nat -S PREROUTING
sudo iptables -t nat -S POSTROUTINGYou should see net.ipv4.ip_forward = 1 and your DNAT and MASQUERADE rules. Each rule should appear once. UFW does not always flush the nat table on reload, so repeated reloads after edits can leave duplicates. If you see copies, a reboot gives you a clean table.
Check it works
Test from a machine outside the VPS, such as your home computer:
curl -I http://203.0.113.10:8080You should get the backend's HTTP response headers. A test from the VPS itself does not pass through PREROUTING, so it will not prove the forward works.
On the backend, check that requests arrive, for example in its web server access log.
Troubleshooting
- UFW will not start after the edit: look for a missing
COMMIT, a typo in the interface name or the nat block placed inside*filter. Restore the backup withsudo cp /etc/ufw/before.rules.bak /etc/ufw/before.rules. - Connection times out: check
ip_forwardis 1 and theufw route allowrule exists (sudo ufw statuslists it underALLOW FWD). - Backend receives packets but no reply comes back: the backend's replies are not returning through the VPS. Keep the MASQUERADE line or fix the backend's routing.
- Works, then breaks after a Docker install: Docker changes the forward policy and adds its own NAT rules. Test again and see Stop Docker from bypassing your UFW firewall.
Next steps
- Do the same with another firewall: Port forwarding and masquerading with firewalld.
