When a packet arrives at a Linux server, the kernel's netfilter subsystem processes it before any userspace application — including your web server, database, or SSH daemon — ever sees it. iptables is the command-line interface to that netfilter framework. A single iptables rule can silently drop every packet from a specific IP address, redirect traffic to a different port, or rate-limit connection attempts — all at kernel speed, before any CPU cycles are spent on application processing. Understanding iptables is fundamental to Linux server security.
Tables and Chains: The Architecture
iptables organizes rules into tables, and each table contains chains. Each chain is an ordered list of rules evaluated against packets as they move through the kernel's network stack.
The three most commonly used tables are:
- filter — the default table. Controls whether packets are accepted or dropped. Most firewall rules live here.
- nat — handles Network Address Translation. Used for port forwarding, masquerading, and DNAT/SNAT rules.
- mangle — modifies packet headers (TTL, TOS/DSCP, marks). Less commonly needed for basic firewalling.
Within the filter table, three built-in chains exist:
- INPUT — packets destined for the local system (the server itself)
- OUTPUT — packets originating from the local system
- FORWARD — packets being routed through the system (the server acting as a router)
Each chain has a default policy — what happens to packets that do not match any rule. Setting INPUT to DROP as the default policy means any packet not explicitly matched by an ACCEPT rule is silently discarded.
How Rules Are Evaluated
When a packet arrives, iptables evaluates the rules in a chain sequentially from top to bottom. The first matching rule determines the packet's fate. Once a rule matches and applies an action (called a target), evaluation stops for that chain. Common targets are:
ACCEPT— allow the packet throughDROP— silently discard the packet; the sender receives no notificationREJECT— discard the packet and send an error back (ICMP port unreachable by default)LOG— write packet details to the kernel log (does not stop processing; packet continues to the next rule)RETURN— stop processing in the current chain and return to the calling chain
Essential iptables Commands
Basic syntax: iptables -t [table] -[A|I|D] [CHAIN] [match criteria] -j [TARGET]
Common examples:
iptables -A INPUT -p tcp --dport 22 -j ACCEPT— allow inbound SSHiptables -A INPUT -p tcp --dport 80 -j ACCEPT— allow inbound HTTPiptables -A INPUT -p tcp --dport 443 -j ACCEPT— allow inbound HTTPSiptables -A INPUT -s 192.168.1.50 -j DROP— drop all traffic from a specific IPiptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT— allow return traffic for existing connectionsiptables -P INPUT DROP— set default INPUT policy to DROP (default deny)iptables -L -v -n --line-numbers— list all rules with packet counters and line numbersiptables -D INPUT 3— delete rule number 3 from INPUT chainiptables -F— flush (delete) all rules in all chains
Stateful Filtering with conntrack
Raw iptables packet filtering evaluates each packet independently. For a web server, this means you need explicit rules for both inbound requests and outbound responses — which quickly becomes unmanageable.
The conntrack (connection tracking) module makes iptables stateful. It tracks the state of TCP connections and UDP exchanges, classifying packets as:
NEW— first packet of a new connectionESTABLISHED— part of an already-tracked connectionRELATED— related to an existing connection (such as FTP data connections or ICMP error responses)INVALID— does not belong to any known connection
A practical stateful INPUT chain for a web server looks like this:
- ACCEPT established and related connections (return traffic)
- ACCEPT inbound TCP port 443
- ACCEPT inbound TCP port 80
- ACCEPT inbound TCP port 22 from management IP
- ACCEPT inbound ICMP echo-request
- DROP invalid packets
- Default policy: DROP
The ESTABLISHED,RELATED rule at the top means the server can initiate outbound connections (to download updates, contact APIs, etc.) and the responses are automatically accepted without needing explicit inbound rules for those return flows.
NAT with iptables
The nat table handles address translation. Common use cases:
- MASQUERADE — used for outbound NAT when the external IP is dynamic (home router or cloud NAT).
iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE - SNAT — outbound NAT with a static source IP. Faster than MASQUERADE for static IPs because it does not need to look up the interface IP for each packet.
- DNAT — destination NAT, used for port forwarding.
iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.100:80forwards external port 8080 to an internal server's port 80.
iptables vs. nftables: What Changed
| Feature | iptables | nftables |
|---|---|---|
| Introduced | Linux kernel 2.4 (2001) | Linux kernel 3.13 (2014) |
| Syntax | Separate commands per table | Unified syntax, single tool |
| Performance | Linear rule matching | Maps and sets for O(1) lookups on large lists |
| IPv4/IPv6 | Separate iptables / ip6tables | Single ruleset handles both |
| Atomic rule updates | No — rules apply one at a time | Yes — batch transactions |
| Backward compatibility | Native | iptables-nft wrapper available |
| Default on modern distros | Being phased out | Default on Debian 10+, RHEL 8+, Ubuntu 20.04+ |
Most modern Linux distributions default to nftables with an iptables compatibility layer. The iptables commands still work on these systems via iptables-nft, which translates iptables syntax to nftables rules internally. For new deployments, nftables is the better choice — but iptables knowledge remains directly applicable because the concepts (tables, chains, targets, matches) are identical.
Making iptables Rules Persistent
iptables rules are stored in memory and lost on reboot by default. To persist them:
- On Debian/Ubuntu: install
iptables-persistentand usenetfilter-persistent save - On RHEL/CentOS: use
iptables-save > /etc/sysconfig/iptablesand ensureiptables.serviceis enabled - Alternatively: write rules to a shell script executed at startup
Always save a copy of your current rules with iptables-save > rules.backup before making significant changes. A mistake that locks you out of SSH is recoverable only through console access.
Common Misconceptions About iptables
Misconception 1: iptables -F makes a server secure
iptables -F flushes all rules but does not change chain default policies. If the default policy was ACCEPT before the flush, flushing rules leaves the server wide open. The flush command is only safe when combined with iptables -P INPUT DROP to enforce a default-deny policy.
Misconception 2: iptables blocks are immediate and guaranteed
iptables rules take effect immediately for new connections. However, existing established connections are tracked by conntrack and will continue to flow if the ESTABLISHED state is accepted — even if you add a DROP rule for that IP. To terminate an existing connection, you need to flush the conntrack table entry as well using conntrack -D in addition to the iptables rule.
Misconception 3: iptables is only for server administrators
iptables rules are applied on every Linux system — including Android devices, embedded Linux systems, network appliances, and container hosts. Kubernetes clusters use iptables rules extensively for service routing (kube-proxy). Docker writes iptables rules for container networking. Understanding iptables is relevant anywhere Linux networking is involved, not just traditional servers.
Misconception 4: Blocking a port with iptables stops an application from running on that port
iptables only controls network packet delivery. An application can still bind to and listen on a port even if iptables is configured to drop inbound packets on that port — the application just never receives the connection attempts. This is actually useful: a database can bind to its default port but be protected from external access without modifying its configuration.
Pro Tips for iptables
- Always add an ACCEPT rule for established and related connections at the very top of your INPUT chain before setting a default-deny policy. Without it, you will lose SSH access to the server when you set
-P INPUT DROPand cannot reconnect to fix it. - Use
iptables -L -v -n --line-numberswhen debugging. The-vflag shows packet and byte counters per rule — invaluable for confirming which rules are actually matching traffic. - Use the LOG target before DROP rules during troubleshooting:
iptables -I INPUT 1 -j LOG --log-prefix "iptables-drop: ". This logs matched packets to/var/log/kern.logorjournalctl -kwithout affecting their fate. - For rate limiting SSH brute force, combine the conntrack module with the limit module:
iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -m limit --limit 3/min -j ACCEPTfollowed by a DROP rule for new SSH connections above that rate. - Never run
iptables -Fon a production server without first saving rules and being prepared for your current default policies. On cloud VMs, a lockout requires contacting the provider's console access. - Script your iptables rules and source-control them. Manually typed rules accumulate inconsistencies. A script with a flush-and-reload approach ensures your rules are always in a known state.
Check your server's current IP and exposed ports to validate your iptables configuration