iptables & nftables Cheatsheet
Listing and Counters
Use this iptables & nftables reference while you build software engineering projects, review code, or refresh the syntax you reach for most.
Reading the current ruleset
sudo iptables -L # human format, resolves names, slow sudo iptables -L -n # numeric, always prefer this sudo iptables -L -n -v # add packet and byte counters sudo iptables -L -n -v --line-numbers sudo iptables -S # save format, exactly what you would type back sudo iptables -S INPUT # one chain only sudo iptables -t nat -L -n -v sudo iptables-save # every table, the canonical dump
| Flag | Effect |
|---|---|
-L | list rules in a readable table |
-S | list rules as -A commands, copy-pasteable |
-n | no DNS or service-name lookups |
-v | verbose, adds counters, interfaces, and options |
-x | exact counter values instead of 1234K |
--line-numbers | number rules, needed for -I and -D by index |
-Z | zero the counters |
Use -S when you want to edit or reproduce rules, and -L -n -v when you want
to understand what traffic is actually hitting them. -L without -n will hang
on a box with broken DNS, which is a classic mid-incident time sink.
Anatomy of the -L output
Chain INPUT (policy DROP 12 packets, 720 bytes) num pkts bytes target prot opt in out source destination 1 18492 2.1M ACCEPT all -- lo * 0.0.0.0/0 0.0.0.0/0 2 91203 14M ACCEPT all -- * * 0.0.0.0/0 0.0.0.0/0 ctstate RELATED,ESTABLISHED 3 42 2520 ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22 4 7 420 REJECT all -- * * 0.0.0.0/0 0.0.0.0/0 reject-with icmp-port-unreachable
| Column | Meaning |
|---|---|
num | rule position, what -D INPUT 3 refers to |
pkts / bytes | matches since the counters were last zeroed |
target | what happens on a match |
prot | protocol, all when unrestricted |
opt | rule options, f marks fragment-only rules |
in / out | interface constraints, * is any |
source / destination | address matches, 0.0.0.0/0 is any |
| trailing text | the match extensions, such as ctstate or dpt:22 |
The policy line also carries counters. policy DROP 12 packets means twelve
packets fell off the end of the chain and were dropped by the policy rather than
by any rule, which is exactly what you want to watch after tightening a ruleset.
Counters as a debugging tool
Counters answer the only question that matters when a rule "does not work": is traffic reaching this rule at all.
sudo iptables -Z # zero all counters in filter sudo iptables -Z INPUT # one chain sudo iptables -Z INPUT 3 # one rule # ... reproduce the problem ... sudo iptables -L INPUT -n -v --line-numbers
| Observation | Conclusion |
|---|---|
| Counter stays at zero | the packet never matches, check protocol, port, interface, direction |
| Counter rises on an earlier rule | something above it is accepting or dropping first |
| Counter rises on your rule, traffic still fails | the problem is elsewhere, such as routing, NAT, or the service not listening |
| Policy counter rises | no rule matched, the default deny caught it |
# watch a single rule live watch -n1 'sudo iptables -L INPUT -n -v --line-numbers | sed -n "1,8p"' # exact byte counts, no K/M rounding sudo iptables -L -n -v -x
Editing rules by position
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT # append to the end sudo iptables -I INPUT -p tcp --dport 80 -j ACCEPT # insert at position 1 sudo iptables -I INPUT 3 -p tcp --dport 80 -j ACCEPT # insert at position 3 sudo iptables -R INPUT 3 -p tcp --dport 8080 -j ACCEPT # replace rule 3 sudo iptables -D INPUT 3 # delete by number sudo iptables -D INPUT -p tcp --dport 80 -j ACCEPT # delete by exact match sudo iptables -C INPUT -p tcp --dport 80 -j ACCEPT # check, exit 0 if present
Order is the whole game. -A puts the rule after any catch-all deny already
at the bottom of the chain, where it will never match, so a rule that "does
nothing" is very often an -A that should have been an -I.
Deleting by number is convenient and dangerous: the numbers shift after every
delete. Delete from the highest number down, or delete by exact match, or use
-C first to confirm you are naming the rule you think you are.
Chains, flushing, and starting over
sudo iptables -N sshguard # create a user chain sudo iptables -E sshguard ssh-in # rename it sudo iptables -F INPUT # flush one chain, keep the policy sudo iptables -F # flush every chain in filter sudo iptables -X sshguard # delete an empty user chain sudo iptables -X # delete all empty user chains sudo iptables -P INPUT ACCEPT # reset a policy
The safe full reset, in the order that cannot lock you out:
sudo iptables -P INPUT ACCEPT sudo iptables -P FORWARD ACCEPT sudo iptables -P OUTPUT ACCEPT sudo iptables -t nat -F sudo iptables -t mangle -F sudo iptables -F sudo iptables -X
Policies go back to ACCEPT first. Flushing a chain whose policy is DROP
removes the rule that was letting your SSH session through and leaves the deny
policy in place, which drops you immediately.
Comments, diffing, and backups
-m comment --commenton every non-obvious ruleiptables-savebefore every edit sessiondifftwo saves-Cbefore-D