Networking & DNS

Port forwarding and NAT with UFW before.rules

Forward a public port on your VPS to a backend server or VPN client with UFW. Turn on IP forwarding, add a nat table to before.rules, allow routed traffic and test from outside.

AdvancedLinux server5 min read

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.2 running 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:
Bash
  ip -br addr

It 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:

Bash
sudo nano /etc/ufw/sysctl.conf

Find the commented line #net/ipv4/ip_forward=1 and remove the #:

net/ipv4/ip_forward=1

If 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:

Bash
grep DEFAULT_FORWARD_POLICY /etc/default/ufw

It 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:

Bash
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:

Bash
sudo nano /etc/ufw/before.rules

At 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
COMMIT

What it does:

  • PREROUTING DNAT rewrites the destination of packets that arrive on eth0 for port 8080, sending them to 10.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 MASQUERADE

The *nat block must have its own COMMIT and must sit outside the *filter section.

Apply the changes

Bash
sudo ufw disable
sudo ufw enable

Then confirm the kernel and NAT settings:

Bash
sysctl net.ipv4.ip_forward
sudo iptables -t nat -S PREROUTING
sudo iptables -t nat -S POSTROUTING

You 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:

Bash
curl -I http://203.0.113.10:8080

You 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 with sudo cp /etc/ufw/before.rules.bak /etc/ufw/before.rules.
  • Connection times out: check ip_forward is 1 and the ufw route allow rule exists (sudo ufw status lists it under ALLOW 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

Was this guide helpful?

Still stuck?

Ask MdmHost AI for a quick answer, or open a ticket and our team will take a look. We are here 24/7.