Why iptables -C Fails 'Bad Rule' on NAT PREROUTING REDIRECT
Fix iptables -C 'Bad rule (does a matching rule exist?)' error for NAT PREROUTING REDIRECT rules visible in -t nat -L. Learn table mismatch, --to-ports syntax, hostname resolution, nftables issues, and exact verification with -S and iptables-save.
Why does iptables -C report ‘Bad rule (does a matching rule exist in that chain?)’ for a REDIRECT rule visible in the NAT PREROUTING chain?
Issue Description:
The rule appears in iptables -t nat -L output:
Chain PREROUTING (policy ACCEPT)
target prot opt source destination
REDIRECT tcp -- 109x194x11x11.static-business.msk.ertelecom.ru anywhere tcp dpt:https redir ports 4444
REDIRECT tcp -- 178.218.11.11 anywhere tcp dpt:https redir ports 4444
...
However, checking the specific rule fails:
root@107976:~# iptables -C PREROUTING -s 178.218.11.11 -p tcp --dport 443 -j REDIRECT --to-port 4444
iptables: Bad rule (does a matching rule exist in that chain?).
What is causing this mismatch? How can I correctly verify if the iptables NAT PREROUTING REDIRECT rule matches the specified criteria?
The iptables -C Bad rule error hits when checking a NAT PREROUTING REDIRECT rule because the command defaults to the filter table—no PREROUTING chain there—and your check uses --to-port 4444 while the actual rule likely specifies --to-ports 4444. That tiny syntax mismatch kills the verification, even though iptables -t nat -L shows the rule clearly. Fix it by always adding -t nat and pulling the exact spec from iptables -S or iptables-save; for your 178.218.11.11 HTTPS redirect, try iptables -t nat -C PREROUTING -s 178.218.11.11 -p tcp --dport 443 -j REDIRECT --to-ports 4444.
Contents
- Why iptables -C Fails with “Bad rule” on NAT PREROUTING REDIRECT Rules
- Understanding iptables Tables: NAT vs Filter and PREROUTING Chain
- Correct Syntax for Checking REDIRECT Rules: --to-ports vs --to-port
- Extracting Exact Rule Syntax with iptables -S and iptables-save
- Handling Hostnames and IP Resolution in iptables Rules
- nftables Backend Issues and iptables-legacy Workaround
- Best Practices: Verify and Test NAT PREROUTING Rules Effectively
- Sources
- Conclusion
Why iptables -C Fails with “Bad rule” on NAT PREROUTING REDIRECT Rules
Ever run iptables -C PREROUTING ... and get slapped with “Bad rule (does a matching rule exist in that chain?)”—even when iptables -t nat -L spits out your REDIRECT rule right there? You’re not alone. This trips up sysadmins daily because iptables -C without -t defaults to the filter table. Filter has INPUT, FORWARD, OUTPUT chains. No PREROUTING. Zip.
Your output shows PREROUTING under NAT: that tcp dpt:https REDIRECT to ports 4444 for 178.218.11.11 (and the hostname version). Perfect for intercepting inbound HTTPS traffic pre-routing decision. But punch in iptables -C PREROUTING -s 178.218.11.11 -p tcp --dport 443 -j REDIRECT --to-port 4444? Boom—filter table lookup fails instantly. No chain, no rule.
A Server Fault thread nails this exact pitfall: user saw NAT rules in -L, but -C bombed without -t nat. Add it, and suddenly your check matches reality. But wait—there’s more. Syntax tweaks (coming up) can still derail it.
Quick test? Run iptables -t filter -L PREROUTING. “No chain/target/match by that name.” That’s your smoking gun.
Understanding iptables Tables: NAT vs Filter and PREROUTING Chain
Iptables isn’t one big rulebook—it’s five tables, each with specialized chains. Filter? Defaults for blocking/allowing traffic (INPUT/FORWARD/OUTPUT). NAT? That’s where magic like PREROUTING lives, rewriting packets before the routing decision.
PREROUTING chain only exists in NAT (and mangle/raw, but NAT’s your spot for REDIRECT). Your rule grabs inbound TCP:443 from 178.218.11.11, bounces it to local port 4444. Why? Proxying HTTPS? MitM setup? Common in reverse proxies or security tools.
Netfilter hook order: PREROUTING (NAT) → routing → INPUT (filter). Miss the table, and -C hunts ghosts. The official iptables man page spells it out: -t table mandatory for non-filter; default is filter since forever.
Picture this table:
| Table | Key Chains | When It Runs | Your REDIRECT Fits? |
|---|---|---|---|
| filter | INPUT, FORWARD, OUTPUT | Post-routing decision | No—blocks packets |
| nat | PREROUTING, INPUT, OUTPUT, POSTROUTING | Pre/post routing | Yes—redirects early |
| mangle | PREROUTING etc. | Marking/TOS tweaks | Maybe, but not here |
No wonder -C without -t nat chokes on PREROUTING.
Correct Syntax for Checking REDIRECT Rules: --to-ports vs --to-port
Syntax is brutal—tiny diffs, massive fails. Your -C uses --to-port 4444. But -L shows “redir ports 4444”. That’s shorthand for --to-ports 4444.
REDIRECT module demands --to-ports (plural) in nat PREROUTING. Supports ranges: --to-ports 1000-2000. --to-port? Invalid there, or single-port only in some contexts. Linux man pages confirm: “REDIRECT --to-ports port[-port]”. Plural. Always.
Your failing command:
iptables -C PREROUTING -s 178.218.11.11 -p tcp --dport 443 -j REDIRECT --to-port 4444
Winning version:
iptables -t nat -C PREROUTING -s 178.218.11.11 -p tcp --dport 443 -j REDIRECT --to-ports 4444
Hostname rule? -s 109x194x11x11.static-business.msk.ertelecom.ru resolves at runtime, but -C needs exact match—IP or resolved form. More on that next.
Test it. If still “Bad rule”? Rule order or partial match. -C requires exact spec match, byte-for-byte.
Extracting Exact Rule Syntax with iptables -S and iptables-save
Guessing syntax? Recipe for pain. Dump the real deal.
First, iptables -t nat -S PREROUTING. Numeric output, full glory:
-A PREROUTING -s 178.218.11.11/32 -p tcp -m tcp --dport 443 -j REDIRECT --to-ports 4444
See the /32? Or -m tcp? -L hides matches/modules. Copy-paste that into -C. Boom—verifies.
Pro move: iptables-save | grep PREROUTING; dumps all rules raw, across tables. Grep your IP or “REDIRECT.*4444”. Cyberciti guide pushes this for NAT hunts—handles huge rulesets.
Example from your setup:
# iptables-save | grep -A2 PREROUTING
-A PREROUTING -s 109.194.11.11.static-business.msk.ertelecom.ru/32 -p tcp --dport 443 -j REDIRECT --to-ports 4444
-A PREROUTING -s 178.218.11.11/32 -p tcp --dport 443 -j REDIRECT --to-ports 4444
Now -C with that exact string. No more “Bad rule”.
Handling Hostnames and IP Resolution in iptables Rules
That first rule: 109x194x11x11.static-business.msk.ertelecom.ru. Hostname in -s? Iptables resolves it at insert time via DNS, stores as IP. But -L shows original hostname (reverse lookup magic).
-C? Must match stored IP form. Run host 109.194.11.11.static-business.msk.ertelecom.ru or dig it. Say it resolves to 109.194.11.11—use -s 109.194.11.11 in check.
Mismatch? “Bad rule”. Tricky if DNS changes post-insert. Always -S for truth.
IPv6? ip6tables -t nat. But your case screams IPv4.
nftables Backend Issues and iptables-legacy Workaround
Modern distros (Ubuntu 20+, RHEL 8+) default to nftables backend for iptables commands. Translation layer glitches: “Bad rule” on NAT checks, especially containers/Docker.
Red Hat details flag this—nft rejects some legacy syntax in checks, even if insert worked. Your hostname rule? nftables might mangle resolution.
Switch: update-alternatives --set iptables /usr/sbin/iptables-legacy. Retry -C. Often fixes. Or go native: nft list table nat. Raw ruleset, no compat woes.
Docker users? Host iptables rarely affects container NAT—check nft list table ip nat.
Best Practices: Verify and Test NAT PREROUTING Rules Effectively
Don’t stop at -C. Full audit:
- List verbose:
iptables -t nat -L PREROUTING -n -v -x. Counters show hits. - Script checks: SuperUser example—
iptables -t nat -C ... 2>/dev/null || echo "Missing". - Test traffic:
nc -l 4444; curl from 178.218.11.11:443. lands on 4444? - Backup:
iptables-save > rules.v4. - Monitor:
watch -n1 'iptables -t nat -L -v -n'.
Edge: Multiport rules? Specify --multiport. Firewalld? firewall-cmd --direct --get-all-rules.
Workflow: -t nat -S → copy → -C → counters → test.
Sources
- Server Fault: iptables -C check doesn’t work — Explains table mismatch causing “Bad rule” for PREROUTING: https://serverfault.com/questions/1083046/iptables-c-check-doesnt-work
- iptables(8) - Linux man page — Official details on tables, chains, and REDIRECT syntax: https://man7.org/linux/man-pages/man8/iptables.8.html
- Linux iptables man page — REDIRECT module specifics including --to-ports requirement: https://linux.die.net/man/8/iptables
- Checking iptables PREROUTING NAT rules — Commands for listing and grepping NAT PREROUTING rules: https://www.cyberciti.biz/faq/checking-list-iptables-prerouting-nat-rules-linux-command/
- Red Hat: Bad rule error with nftables — nftables compatibility issues in RHEL with iptables NAT: https://access.redhat.com/solutions/6514071
- SuperUser: Check if iptables rule exists — Scripting safe rule checks with error suppression: https://superuser.com/questions/360094/how-can-i-check-if-an-iptables-rule-exists
Conclusion
Bottom line: iptables -C Bad rule for NAT PREROUTING REDIRECT stems from missing -t nat, --to-port vs --to-ports, or backend quirks—fix with exact -S dumps. Grab iptables -t nat -S PREROUTING, paste into -C, watch it succeed. Pro tip: Script it, check counters, test live traffic. You’ll nail these redirects every time, no more head‑scratching.