iptables NAT规则不生效主因是链触发时机与数据包流向不匹配:PREROUTING在路由前处理入向包(需匹配真实入接口且ip_forward=1),POSTROUTING在路由后处理出向包(需匹配最终出口接口),接口条件必须按实际转发路径设置,不可凭IP或服务猜测。

iptables 的 NAT 规则不生效,常见原因不是规则写错了,而是链的触发时机、数据包流向和接口匹配没对上。PREROUTING 和 POSTROUTING 属于 nat 表的两个关键链,它们在数据包生命周期中位置固定,但是否命中,取决于数据包实际经过的网络路径和 -i / -o 接口条件是否匹配。
PREROUTING 链只处理“刚进设备”的入向数据包
PREROUTING 在路由决策前触发,适用于目标地址需要修改(如 DNAT)的场景,比如把发往本机某端口的请求转给内网服务器。但它**只匹配进入该网络接口的数据包**,且仅对非本机发起、目的地为本机或需转发的数据包生效。
- 若数据包是从本机进程发出(如 curl 本地地址),它根本不会经过 PREROUTING,而是走 OUTPUT 链
- 若用 -i eth0 但数据包实际从 docker0 或 lo 进来,规则不匹配
- 转发未开启(net.ipv4.ip_forward=0)时,即使写了 PREROUTING DNAT,系统也不会把包送进转发流程,DNAT 后续也无意义
POSTROUTING 链只处理“即将发出去”的出向数据包
POSTROUTING 在路由决策后、离开网络接口前触发,常用于 SNAT/MASQUERADE,比如让内网机器通过网关上网。它的关键点是:必须匹配数据包**最终选择的出口接口**(-o),而不是源地址或中间桥接设备。
- 若写 -o eth0,但因路由表选择走 bond0 或 wg0 出去,规则跳过
- Docker 或 Podman 默认使用 docker0 桥,容器访问外网时,POSTROUTING 匹配的是宿主机的物理出口(如 eth0),而非 docker0;但若启用了 hairpin 或 host-network 模式,路径可能不同
- MASQUERADE 依赖 -o,且只在动态 IP(如 DHCP)场景比 SNAT 更安全;但若 -o 条件写错,它就静默不生效
接口绑定要按真实转发路径写,不能凭 IP 或服务猜
很多人习惯根据服务监听 IP 或客户端来源写 -i,但 iptables 不看“谁连的”,而看“包从哪个接口进来、从哪个接口出去”。验证方法很简单:
- 用 tcpdump 抓包确认流量实际进出的接口:tcpdump -i eth0 port 80
- 查路由:ip route get 8.8.8.8 看出口设备;查反向路径:ip route get from 192.168.1.100 to 10.0.0.5
- 启用日志临时排查:-j LOG --log-prefix "NAT-DEBUG: ",配合 dmesg | tail 看哪条规则被命中
常见配置陷阱与检查清单
以下情况会导致 NAT 规则看似存在却无效:
- nat 表规则放在 filter 表里(比如误用 -A INPUT 而非 -t nat -A PREROUTING)
- 规则插入顺序错误:靠前的 ACCEPT 或 RETURN 提前终止了链遍历
- 使用了 -s 或 -d 匹配,但值与实际包头不符(如 DNAT 后目的 IP 已变,PREROUTING 中 -d 是原始目的,POSTROUTING 中 -s 是原始源)
- 容器环境绕过 iptables(如启用 nftables 后台、firewalld 管理、或使用 net=host)










