Stateful vs. stateless: why most rulesets are shorter than you'd expect

A purely stateless filter examines every packet in isolation — you'd need one rule allowing your outbound request and a separate rule allowing the reply back in. Almost every real firewall instead uses connection tracking (conntrack): once a connection is approved going out, its replies are automatically recognized and let back in, without a matching rule for them. That's why a working ruleset usually just needs one rule near the top: allow anything ESTABLISHED or RELATED, then a handful of rules for genuinely new inbound connections.

How a firewall evaluates a packet: order is everything

Unlike routing, where the most specific match always wins regardless of table order, firewall rules are evaluated top to bottom and the first match wins — full stop. A broad ACCEPT rule sitting above a specific DROP rule makes that DROP unreachable, no matter how precisely it's written. Edit the chain below and test packets against it:

#MatchAction
Default policy (if nothing above matches):

Add a rule

Test a packet

Writing rules: the actual tools

ToolWhat it isExample: allow SSH, block one IP
iptables The long-standing Linux packet filter frontend, still the most widely deployed. iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -s 198.51.100.5 -j DROP
nftables iptables' designated successor, one unified tool for IPv4, IPv6, and more. nft add rule inet filter input tcp dport 22 accept
nft add rule inet filter input ip saddr 198.51.100.5 drop
ufw "Uncomplicated Firewall" — a friendly frontend over iptables, standard on Ubuntu. ufw allow 22/tcp
ufw deny from 198.51.100.5
firewalld Zone-based management, standard on RHEL/Fedora/CentOS. firewall-cmd --add-service=ssh --permanent
firewall-cmd --add-rich-rule='rule family="ipv4" source address="198.51.100.5" reject' --permanent
Cloud security groups AWS/Azure/GCP's built-in filtering — stateful, but allow-only: there's no explicit DROP/REJECT rule, anything not listed is denied by default. Add inbound rule: TCP 22 from 203.0.113.0/24 → allow
(there is no "deny 198.51.100.5" rule to add)

Useful housekeeping commands: iptables -L INPUT --line-numbers to see rule positions, iptables -D INPUT 3 to delete rule 3, iptables -I INPUT 1 ... to insert at the very top (since, as above, position decides everything).

Common gotchas