Cheat Sheet

iptables Commands Cheat Sheet

iptables is the classic Linux firewall tool, still everywhere even though nftables is its successor. This sheet covers listing and adding rules, opening and blocking ports, NAT and port forwarding, making rules survive a reboot, and how to flush without locking yourself out of SSH.

Last updated September 5, 2026

Listing Rules

Everything needs root. skips DNS lookups, adds packet counters, gives you the numbers used for deleting.

CommandWhat it does
All rules in the filter table, numeric, with counters
Same, with rule numbers for deleting
One chain only
Rules printed as the commands that made them
Rules in the nat table (NAT rules never show in the default listing)
Find rules touching port 22
Watch packet counters move, live

Tables and Chains

A rule lives in a chain, a chain lives in a table. is the default, which is why NAT rules seem to disappear until you ask for their table.

Table / chainWhat it is for
tableAccept or drop traffic; the default table
tableRewrite addresses and ports (MASQUERADE, DNAT)
tableAlter packet headers (TTL, marks); rarely needed
chainTraffic addressed to this machine
chainTraffic this machine sends
chainTraffic routed through this machine (routers, Docker)
chainBefore routing; where DNAT and port forwards go
chainAfter routing; where MASQUERADE goes

Allowing Traffic

The order matters: rules match top down, first match wins. Allow SSH before anything drops.

CommandWhat it does
Allow SSH
Allow HTTP ( for HTTPS)
Both in one rule
Allow replies to connections this box started (put this near the top)
Allow loopback (many things break without it)
Allow everything from one IP
Allow one subnet to reach Postgres
Allow a UDP port (WireGuard here)

A minimal safe INPUT policy, in the right order:

sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPT
sudo iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
sudo iptables -P INPUT DROP

The firewall rule generator builds rule sets like this from a checklist.

Blocking Traffic

CommandWhat it does
Block one IP
Block a range
Block it ahead of every allow rule
Block a port silently
Block a port with an immediate error
Stop this box talking to an IP

DROP discards the packet silently, so the client hangs until it times out; REJECT answers with an error, so the client fails fast with connection refused. DROP gives port scanners nothing, REJECT is kinder for services your own users hit. If SSH suddenly gets connection refused, look for a REJECT rule.

Adding and Deleting Rules

appends to the bottom, inserts at the top (or a position). Since first match wins, a rule appended after a DROP does nothing.

CommandWhat it does
Append at the end of the chain
Insert at position 1, the very top
Insert at position 3
Delete by repeating the rule exactly
Get the numbers, then...
Delete rule number 3
Replace rule number 3
Set the chain policy, the fate of packets no rule matched

Rule numbers shift after every delete. Deleting several by number goes highest first, or re-list between deletes.

Flushing Rules

deletes all rules but does not touch chain policies. If the policy is DROP, flushing locks you out of SSH instantly. Safe order:

# 1. Open the policies first, so an empty ruleset allows everything:
sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT
# 2. Now flush:
sudo iptables -F
sudo iptables -X          # delete custom chains
sudo iptables -t nat -F   # nat and mangle have their own rules
sudo iptables -t mangle -F

Keep the current SSH session open and test a fresh login before closing it. When rebuilding, the first rule you add back is the SSH allow.

NAT and Port Forwarding

NAT rules live in the table and need forwarding enabled in the kernel first:

sudo sysctl -w net.ipv4.ip_forward=1
# permanent: echo 'net.ipv4.ip_forward=1' | sudo tee /etc/sysctl.d/99-forward.conf
CommandWhat it does
Share this box's IP with everything it routes (the home router rule)
Masquerade one subnet only (typical VPN setup)
Redirect a port on this box
See what NAT is doing

Forward port 80 arriving on this box to an internal server:

sudo iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination 10.0.0.5:80
sudo iptables -A FORWARD -p tcp -d 10.0.0.5 --dport 80 -m conntrack --ctstate NEW,ESTABLISHED,RELATED -j ACCEPT
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE

Saving Rules

Rules live in kernel memory and are gone after a reboot unless something restores them.

CommandWhat it does
Dump current rules to a file
Load them back
Debian/Ubuntu: restores rules at boot
Save current rules for that boot restore
RHEL/CentOS with the iptables-services package
IPv6 rules are saved separately
Apply with an automatic rollback if you do not confirm (lockout insurance)

Logging and Rate Limiting

CommandWhat it does
Rate limit new SSH connections
Track new SSH sources...
...and drop an IP with 5 attempts in a minute
Log what falls through (place just before the end or a DROP)
Same, without flooding the log

Logged packets land in the kernel log: or .

nftables and Front Ends

nftables is the successor; new setups should use it or a front end rather than raw iptables.

CommandWhat it does
in the output means your commands become nftables rules
Show a rule in nft syntax
See everything, including rules ufw, firewalld, and Docker added
Debian/Ubuntu: switch between iptables-nft and iptables-legacy

Convert whole rule sets with the iptables to nftables converter. If all you want is ports opened and closed on one server, UFW is the simpler front end and manages this layer for you.

Gotchas

  • Mixing tools bites: ufw, firewalld, and Docker all write their own iptables or nftables rules, so a rule you add by hand can be shadowed by theirs or wiped on their reload. shows the whole truth; pick one manager per box.
  • Rules are gone after a reboot unless you set up persistence. The classic failure: firewall works for months, a reboot silently drops it.
  • without hides all NAT rules, and without it stalls on DNS lookups.
  • iptables-legacy and iptables-nft keep separate rule sets. If rules seem to be missing, the other binary may own them: check and .
  • Docker publishes container ports by writing its own FORWARD and DOCKER chain rules, bypassing your INPUT rules entirely. A "closed" port can still be reachable through Docker.
  • IPv6 is a separate firewall ( or the nft family). Locking down IPv4 while the service also listens on IPv6 locks down half the door.

iptables Cheat Sheet FAQ

How do I make iptables rules persistent after a reboot?
By default they vanish at reboot; nothing saves them for you. On Debian and Ubuntu, install iptables-persistent (sudo apt install iptables-persistent), which saves current rules during install and reloads them at boot; after changes, run sudo netfilter-persistent save. On RHEL, CentOS, and Fedora the package is iptables-services: enable it, then sudo service iptables save writes to /etc/sysconfig/iptables. The portable route on any distro is iptables-save > /etc/iptables/rules.v4 plus a systemd unit or if-up script that runs iptables-restore. IPv6 rules are a separate table saved with ip6tables-save.
Why does Debian say iptables: command not found?
Two different causes. On minimal Debian, Raspberry Pi OS, and container images, the package simply is not installed: sudo apt install iptables fixes it. More often the binary exists but lives in /usr/sbin, which is not on a normal user's PATH, so a plain iptables -L says command not found while sudo iptables -L works fine. Since you need root for iptables anyway, the habit of always typing sudo makes the problem disappear. Note that installing iptables on a modern distro usually gets you iptables-nft, a compatibility layer that writes nftables rules underneath.
How do I check if iptables is running?
iptables is not a service you start and stop; the rules live in the kernel and are always active. The question is what the rules currently are: sudo iptables -L -n -v shows them with packet counters, and sudo iptables -S prints them as commands. Empty chains with policy ACCEPT mean the firewall is effectively off. To see whether anything else manages the rules, check systemctl status ufw, firewalld, and netfilter-persistent, and run sudo iptables -V: a version marked (nf_tables) means your iptables commands are being translated into nftables rules.
How do I flush iptables rules without locking myself out over SSH?
The lockout happens when the INPUT policy is DROP and you flush the rules, because flushing removes the rule that allows SSH while the DROP policy stays. Do it in this order: first set the policies to ACCEPT (iptables -P INPUT ACCEPT, then FORWARD and OUTPUT the same), then iptables -F to flush and iptables -X to delete custom chains. With policies at ACCEPT, an empty ruleset lets everything through instead of nothing. When building the new rules, allow SSH first (-A INPUT -p tcp --dport 22 -j ACCEPT) before any DROP rule or policy, and keep your current session open while you test a new connection.
Should I use iptables or nftables?
For anything new, nftables: it replaced iptables as the kernel's firewall front end, and Debian, Ubuntu, and RHEL already translate iptables commands into nftables rules via iptables-nft. Existing iptables scripts keep working through that layer, so there is no urgency to rewrite, but new rule sets are cleaner in native nft syntax, and iptables-translate or the iptables to nftables converter will convert what you have. For a single server where you just want ports opened and closed, UFW is the simpler answer, and it drives nftables or iptables for you. Whichever you pick, pick one: rules from two tools stack in unpredictable ways.

Related cheat sheets