Destination NAT changes where matching packets are sent after they arrive at a Linux gateway. With iptables, a DNAT rule can accept traffic on a gateway address and port, then rewrite the destination to an internal service before the packet is routed.
DNAT belongs in the nat table's PREROUTING chain for traffic arriving from another host. The translated packet must still be allowed through the FORWARD chain, and the backend must route replies back through the gateway unless another source NAT rule is intentionally used.
Examples below use wan0 as the outside interface, lan0 as the backend-side interface, gateway port 8080, and backend 10.10.0.20:80. Replace those values before running commands, keep a rollback path open, and save rules only after an outside client request and the NAT counters prove the translation matched.
$ ip -br addr lo UNKNOWN 127.0.0.1/8 ::1/128 wan0 UP 203.0.113.10/24 lan0 UP 10.10.0.1/24
wan0 receives client traffic. lan0 reaches the backend network.
$ curl -sS http://10.10.0.20/ backend service through gateway
Fix listener, routing, or application errors before adding DNAT. A destination rewrite cannot make a closed backend service answer.
$ sudo iptables-save > ~/iptables.before-dnat.rules
Keep a console session or known-good remote session open. Restore with sudo iptables-restore < ~/iptables.before-dnat.rules if the change breaks required traffic.
$ sudo sysctl -w net.ipv4.ip_forward=1 net.ipv4.ip_forward = 1
This lets the host route IPv4 packets between interfaces when routes and firewall rules allow it. Do not enable forwarding on a host that should remain a single-interface endpoint.
$ sudo iptables -t nat -A PREROUTING -i wan0 -p tcp --dport 8080 -j DNAT --to-destination 10.10.0.20:80
The rule changes the packet destination before route lookup. It does not by itself permit forwarding through the filter table.
$ sudo iptables -A FORWARD -i wan0 -o lan0 -p tcp -d 10.10.0.20 --dport 80 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT
$ sudo iptables -A FORWARD -i lan0 -o wan0 -p tcp -s 10.10.0.20 --sport 80 -m conntrack --ctstate ESTABLISHED -j ACCEPT
The backend must send replies through this gateway for the client to see a complete connection. Use a scoped SNAT or masquerade rule only when backend routing cannot be fixed or client addresses do not need to be preserved.
$ sudo iptables -t nat -S PREROUTING -P PREROUTING ACCEPT -A PREROUTING -i wan0 -p tcp -m tcp --dport 8080 -j DNAT --to-destination 10.10.0.20:80
$ sudo iptables -S FORWARD -P FORWARD DROP -A FORWARD -d 10.10.0.20/32 -i wan0 -o lan0 -p tcp -m tcp --dport 80 -m conntrack --ctstate NEW,ESTABLISHED -j ACCEPT -A FORWARD -s 10.10.0.20/32 -i lan0 -o wan0 -p tcp -m tcp --sport 80 -m conntrack --ctstate ESTABLISHED -j ACCEPT
$ curl -sS http://203.0.113.10:8080/ backend service through gateway
A test from the gateway itself can miss interface, upstream firewall, and routing problems. Use a client that reaches the gateway the same way real users do.
$ sudo iptables -t nat --list PREROUTING --line-numbers --numeric --verbose --exact Chain PREROUTING (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 1 60 DNAT tcp -- wan0 * 0.0.0.0/0 0.0.0.0/0 tcp dpt:8080 to:10.10.0.20:80
The pkts and bytes counters increase when traffic reaches the gateway port and matches the translation rule.
$ sudo netfilter-persistent save run-parts: executing /usr/share/netfilter-persistent/plugins.d/15-ip4tables save
Save only after the outside client test, DNAT counter, and forward counters all succeed.
Related: How to save iptables rules permanently