Source NAT rewrites the source address on packets leaving a Linux gateway. With iptables, an SNAT rule gives a private subnet a specific outgoing address when the gateway has a static upstream address and policy needs to name that address directly.
SNAT belongs in the nat table's POSTROUTING chain because the outgoing interface and route are known by then. The FORWARD chain still decides whether private clients may cross the gateway, so translation and forwarding rules must both be present.
Examples below use a private client subnet, an upstream interface, and a static translated source address. Use MASQUERADE instead of SNAT when the upstream address changes dynamically, and save rules only after a private client test and the POSTROUTING counter prove the rule matched.
$ ip -br addr lo UNKNOWN 127.0.0.1/8 ::1/128 lan0 UP 10.10.0.1/24 wan0 UP 203.0.113.25/24
lan0 receives traffic from private clients. wan0 owns the static address used for translated outbound packets.
$ ip addr show dev wan0
3: wan0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500
inet 203.0.113.25/24 scope global wan0
SNAT should name an address that remains valid on the upstream path. Use MASQUERADE for DHCP, PPP, tunnel, or other changing upstream addresses.
$ sudo sysctl -w net.ipv4.ip_forward=1 net.ipv4.ip_forward = 1
This immediately allows the host to route IPv4 packets between interfaces when firewall rules and routes permit it.
$ sudo iptables -A FORWARD -s 10.10.0.0/24 -o wan0 -j ACCEPT
$ sudo iptables -A FORWARD -d 10.10.0.0/24 -i wan0 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
Use stricter interface, destination, or service matches when the gateway policy requires them. A broad FORWARD allow is acceptable only when it matches the intended private subnet policy.
$ sudo iptables -t nat -A POSTROUTING -s 10.10.0.0/24 -o wan0 -j SNAT --to-source 203.0.113.25
SNAT names the translated source address explicitly. It differs from MASQUERADE, which uses the current address on the outgoing interface.
$ sudo iptables -t nat -S POSTROUTING -P POSTROUTING ACCEPT -A POSTROUTING -s 10.10.0.0/24 -o wan0 -j SNAT --to-source 203.0.113.25
$ sudo iptables -S FORWARD -P FORWARD DROP -A FORWARD -s 10.10.0.0/24 -o wan0 -j ACCEPT -A FORWARD -d 10.10.0.0/24 -i wan0 -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
$ curl -sS https://ifconfig.example.net/ 203.0.113.25
Use a service that reports the observed source address, or verify the source on the upstream firewall logs. The reported address should match the SNAT address.
$ sudo iptables -t nat --list POSTROUTING --line-numbers --numeric --verbose --exact Chain POSTROUTING (policy ACCEPT 0 packets, 0 bytes) num pkts bytes target prot opt in out source destination 1 6 504 SNAT all -- * wan0 10.10.0.0/24 0.0.0.0/0 to:203.0.113.25
A zero counter after a successful client test usually means the client did not use this gateway, the source CIDR is wrong, or the outgoing interface name does not match the real route.
$ sudo netfilter-persistent save run-parts: executing /usr/share/netfilter-persistent/plugins.d/15-ip4tables save
Persist SNAT only after the private client test reports the intended translated address.
Related: How to save iptables rules permanently