iptables Command in Linux: Manage Firewall Rules
When a packet arrives at a Linux machine, the kernel decides what to do with it based on a set of firewall rules. Those rules live in the kernel’s Netfilter framework, and for more than two decades the standard way to edit them from user space has been the iptables command.
iptables controls packet filtering, network address translation, and packet mangling. Higher-level front ends such as ufw
and firewalld manage the same Netfilter firewall stack through simpler interfaces, although many modern systems use nftables underneath. Understanding the underlying command is still valuable, especially when you inherit a server that was set up by someone else. This guide walks through the concepts and the commands you need to read, edit, and persist firewall rules.
Tables and Chains
Before you write a rule, you need to know where it goes. iptables is organized into tables, and each table contains chains.
The three tables you will use most often are:
-
filter- the default table, used for allowing and blocking traffic -
nat- used for network address translation, such as port forwarding and masquerading -
mangle- used to alter packet headers, for example to set QoS marks
Each table has a set of built-in chains that correspond to moments in the life of a packet. In the filter table:
-
INPUT- packets destined for the local machine -
OUTPUT- packets originating from the local machine -
FORWARD- packets routed through the machine
A rule says: for packets that enter this chain and match these criteria, take this action. The action is called a target and is usually ACCEPT, DROP, REJECT, or the name of another chain.
iptables Syntax
The general form of the command is:
iptables [-t TABLE] COMMAND CHAIN [MATCH] [-j TARGET]If -t is omitted, iptables uses the filter table. Common commands include -A (append a rule), -I (insert), -D (delete), -L (list), -F (flush), and -P (set default policy).
All commands that change the firewall require root privileges. Run them with sudo or as root.
iptables-apply, which rolls back automatically if you lose access.List Rules
To print every rule in the filter table, use the -L option:
sudo iptables -LThe default output shows service names, resolves IP addresses, and hides packet and byte counters. For real work, add -n to keep numeric output and -v to show counters and interface information:
sudo iptables -L -n -vChain INPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
1234 98K ACCEPT tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:22
0 0 DROP tcp -- * * 0.0.0.0/0 0.0.0.0/0 tcp dpt:23
Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
Chain OUTPUT (policy ACCEPT 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
To list a single chain, append its name:
sudo iptables -L INPUT -n -vTo number the rules so you can reference them by index when deleting, add --line-numbers:
sudo iptables -L INPUT -n -v --line-numbersAdd and Remove Rules
New rules are added to the end of a chain with -A (append) or at a specific position with -I (insert). The difference matters because iptables evaluates rules top to bottom and stops at the first match.
To allow incoming SSH connections:
sudo iptables -A INPUT -p tcp --dport 22 -j ACCEPTTo allow HTTP and HTTPS:
sudo iptables -A INPUT -p tcp --dport 80 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPTTo insert a rule as the first one in the chain, use -I CHAIN 1:
sudo iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPTThis is important when you are working over SSH. If a broad DROP rule already appears earlier in the chain, appending the accept rule after it would not help because the packet would be dropped first.
To delete a rule, either repeat the exact specification with -D:
sudo iptables -D INPUT -p tcp --dport 23 -j DROPOr delete by line number, which is easier when the rule has many options:
sudo iptables -D INPUT 3Allow and Block Specific IPs
To block all traffic from a single IP address:
sudo iptables -A INPUT -s 203.0.113.10 -j DROPTo block a range using CIDR notation:
sudo iptables -A INPUT -s 203.0.113.0/24 -j DROPTo allow SSH only from a trusted subnet:
sudo iptables -A INPUT -p tcp -s 192.168.1.0/24 --dport 22 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 22 -j DROPThe first rule accepts SSH from the local subnet. The second drops SSH from everywhere else. Order matters: if you reversed the two lines, every SSH attempt would be dropped before the accept rule had a chance to match.
Allow Established Connections
Most firewall setups include a rule that accepts traffic belonging to an already established connection. This lets return traffic through without needing a matching rule for each outbound request:
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPTPut this rule near the top of the INPUT chain so it matches early. Without it, the default DROP policy breaks outbound connections that expect responses.
Set a Default Policy
Each built-in chain has a default policy that applies when no rule matches. The -P option changes it:
sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPTSwitching INPUT to DROP is the foundation of a deny-by-default firewall: nothing gets in unless an explicit rule allows it. Before you flip the policy, make sure you have already added the rules that allow SSH, established connections, and anything else you need.
Flush Rules
To remove every rule from every chain in the current table:
sudo iptables -FTo flush a specific chain only:
sudo iptables -F INPUTFlushing does not reset the default policies. If you have set INPUT to DROP, flushing will leave it at DROP with no rules, which blocks all inbound traffic. Reset the policy to ACCEPT first if that is not what you want:
sudo iptables -P INPUT ACCEPT
sudo iptables -FSave and Restore Rules
Rules added with iptables live in kernel memory only. They disappear on reboot unless you save them.
On Ubuntu, Debian, and Derivatives, the iptables-persistent package saves rules to /etc/iptables/rules.v4 and reloads them at boot:
sudo apt install iptables-persistentThe installer asks whether to save the current rules. To update the saved copy later:
sudo netfilter-persistent saveOn Fedora, RHEL, and Derivatives, the equivalent service is iptables-services:
sudo dnf install iptables-services
sudo systemctl enable --now iptables
sudo service iptables saveIndependent of the distribution, you can dump and restore rules manually with iptables-save and iptables-restore:
sudo iptables-save -f /etc/iptables/rules.v4
sudo iptables-restore /etc/iptables/rules.v4This is also the recommended way to edit a large ruleset: save to a file, edit the file, then restore it atomically.
Troubleshooting
Rules disappear after a rebootiptables rules are not persistent by default. Install iptables-persistent on Debian-based systems or iptables-services on RHEL-based ones, and save the ruleset.
SSH stops working after setting a DROP policy
You switched INPUT to DROP without an ACCEPT rule for port 22, or the ACCEPT rule is positioned after a more general DROP rule. Connect through the console, add the rule with -I INPUT 1, and save.
A rule looks correct but does not match
Check the order. iptables walks the chain top to bottom and stops at the first match, so an earlier accept or drop may be catching the packet first. Use iptables -L INPUT -n -v --line-numbers to inspect the order.
Changes are silently ignored
You may be editing the wrong table. A rule in filter does not affect NAT, and vice versa. Pass -t TABLE explicitly when you are not working in filter.
iptables: command not found
On some modern distributions, only nftables is installed by default. Install iptables with your package manager, or use nft directly.
Quick Reference
For a printable quick reference, see the iptables cheatsheet .
| Action | Command |
|---|---|
| List rules (verbose, numeric) | iptables -L -n -v --line-numbers |
| Allow port | iptables -A INPUT -p tcp --dport PORT -j ACCEPT |
| Block IP | iptables -A INPUT -s IP -j DROP |
| Allow established connections | iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT |
| Insert rule at top | iptables -I INPUT 1 -p tcp --dport 22 -j ACCEPT |
| Delete rule by number | iptables -D INPUT N |
| Set default policy | iptables -P INPUT DROP |
| Flush all rules | iptables -F |
| Save rules | iptables-save -f /etc/iptables/rules.v4 |
| Restore rules | iptables-restore /etc/iptables/rules.v4 |
FAQ
Is iptables still relevant in 2026?
It is still installed and widely used, but it is being replaced by nftables, which offers a cleaner syntax and better performance. On recent distributions, the iptables command is often a compatibility front end that writes nftables rules underneath.
Should I use iptables, ufw, or firewalld?
If you are managing rules by hand on Debian or Ubuntu, ufw
is simpler and covers most cases. On Fedora and RHEL, firewalld is the default. Reach for raw iptables when you need fine-grained control that the front ends do not expose, or when you are troubleshooting an existing ruleset.
What is the difference between DROP and REJECT?DROP silently discards the packet; the sender sees a timeout. REJECT sends an ICMP error back (or a TCP reset for TCP), so the sender gets immediate feedback. DROP is often preferred on public interfaces because it does not confirm that the port exists.
How do I block a country or a list of IPs?
For a handful of addresses, add one -s rule per entry. For larger lists, use the ipset tool to manage the addresses and reference the set from a single iptables rule.
Does iptables handle IPv6?
No. Use ip6tables for IPv6 rules. It has the same syntax and the same tables and chains, but operates on a separate rule set.
Conclusion
iptables is a low-level but reliable way to read and shape the Linux firewall. Once you have a working ruleset, save it with iptables-save and commit the file so the next person to touch the server has a clear starting point.
![]()