A firewall isn’t just a gatekeeper; it’s a sophisticated traffic director, and iptables is the maestro.

Let’s get a production web server locked down. We’re going to use iptables to block everything by default and only allow specific, necessary traffic. This is the most secure starting point.

First, we need to flush any existing rules and set default policies. This ensures we start from a clean slate.

iptables -F
iptables -X
iptables -t nat -F
iptables -t nat -X
iptables -t mangle -F
iptables -t mangle -X
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
  • iptables -F: Flushes all rules from all chains.
  • iptables -X: Deletes all non-default chains.
  • iptables -t nat -F, iptables -t nat -X: Does the same for the NAT table.
  • iptables -t mangle -F, iptables -t mangle -X: Does the same for the mangle table.
  • iptables -P INPUT DROP: This is crucial. It sets the default policy for incoming traffic to DROP, meaning any packet not explicitly allowed will be discarded.
  • iptables -P FORWARD DROP: Blocks any traffic trying to be routed through this server.
  • iptables -P OUTPUT ACCEPT: Allows all outgoing traffic. For a web server, this is usually fine as it needs to send responses. We could lock this down further, but it adds complexity and is less common for basic web server setups.

Now, let’s allow essential loopback traffic. This is for internal communication within the server itself.

iptables -A INPUT -i lo -j ACCEPT
iptables -A OUTPUT -o lo -j ACCEPT
  • -i lo: Specifies the loopback interface (lo).
  • -j ACCEPT: Allows packets coming in or going out via the loopback interface.

Next, we’ll allow established and related incoming connections. This is vital because when your server initiates an outgoing connection (like sending a response to a client), you want to allow the reply traffic back in.

iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
  • -m conntrack: Loads the connection tracking module.
  • --ctstate ESTABLISHED,RELATED: Matches packets that are part of an existing connection or are related to an existing connection (like FTP data connections).

Now, let’s open up HTTP and HTTPS. This is the core of our web server.

iptables -A INPUT -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -p tcp --dport 443 -j ACCEPT
  • -p tcp: Specifies the TCP protocol.
  • --dport 80: Matches packets destined for port 80 (HTTP).
  • --dport 443: Matches packets destined for port 443 (HTTPS).

If you need SSH access for management, add this rule. Be extremely careful with SSH. It’s often a good idea to restrict SSH to specific IP addresses if possible.

iptables -A INPUT -p tcp --dport 22 -j ACCEPT
  • --dport 22: Matches packets destined for port 22 (SSH).

To make these rules persistent across reboots, you’ll need to save them. The command varies by distribution.

On Debian/Ubuntu:

sudo apt-get install iptables-persistent
sudo netfilter-persistent save

On RHEL/CentOS/Fedora:

sudo service iptables save
# Or for newer systems using firewalld (though we're using iptables directly here):
# sudo firewall-cmd --runtime-to-permanent
# sudo systemctl enable firewalld

The most surprising truth about firewalls is that they don’t actually "block" traffic in the way a physical barrier does; they simply decide not to forward or accept packets based on a set of criteria. A DROP target means the packet is silently discarded. No error message is sent back to the sender. This makes it harder for an attacker to map out your network, as they won’t get connection refused errors for blocked ports.

Let’s consider a common scenario: you’ve set up these rules, and suddenly your web server isn’t responding. You check iptables -L -v -n and everything looks correct. The INPUT chain has DROP as its default, but you see your rules for ports 80 and 443. The trick is often in the OUTPUT chain or, more subtly, in the conntrack state. If your web server needs to make outgoing connections to fetch dynamic content (e.g., to a database or an external API), and your OUTPUT policy was DROP (which we set to ACCEPT here for simplicity), those outgoing requests would fail. The server would never receive the replies because the initial outgoing packets were blocked. The ESTABLISHED,RELATED rule is your best friend here because it allows the return traffic for any connection that the server itself initiated.

If you were to accidentally block loopback traffic, you’d find that many local services simply wouldn’t start or communicate correctly. For instance, a web application might try to communicate with a local database over 127.0.0.1:5432, and if that traffic is dropped by iptables, the application would appear to be broken, even though external access to port 80/443 is fine.

After implementing these rules, the next thing you might discover is that your server’s DNS resolution is failing if you didn’t explicitly allow outgoing UDP traffic on port 53.

iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
iptables -A OUTPUT -p tcp --dport 53 -j ACCEPT

Want structured learning?

Take the full Iptables course →