Back to tutorials
Tutorial

IPTables Firewall Tutorial (2026): Build a Production Web-Hosting Ruleset for a VPS or Dedicated Server

IPTables firewall tutorial (2026) for VPS hosting: safe SSH access, web ports, rate limits, logging, and persistence.

By Anurag Singh
Updated on Jun 06, 2026
Category: Tutorial
Share article
IPTables Firewall Tutorial (2026): Build a Production Web-Hosting Ruleset for a VPS or Dedicated Server

Most hosting outages don’t start with a zero-day. They start with a “quick firewall tweak” that drops your SSH session and strands you outside the box.

This iptables firewall tutorial shows a production-safe way to build and apply a web-hosting ruleset on a VPS or dedicated server in 2026. The goal is simple: change rules without nuking active connections.

The examples assume Ubuntu Server 24.04/26.04, Debian 12/13, AlmaLinux 9/10, or Rocky Linux 9/10. You’ll configure IPv4 and IPv6 rules, keep SSH reachable, and open HTTP/HTTPS. You’ll also add light abuse resistance and make the rules survive reboots.

What you’ll build (and why it fits hosting)

  • A default-deny firewall that still allows SSH, HTTP, HTTPS, and optional control panel ports.
  • Rules that won’t interrupt existing connections while you apply changes.
  • Basic mitigation for noisy traffic (simple rate limits and bad-packet drops).
  • Persistent configuration for both IPv4 and IPv6.
  • A rollback plan so you can recover fast if you mis-type a rule.

If you host client sites or run production WordPress, do this on a VPS with predictable networking and real console access. A HostMyCode VPS is a good fit for hands-on administration like this.

If you don’t want to maintain firewall rules yourself, managed VPS hosting can handle it for you.

Before you touch iptables: two safety nets

Spend five minutes here. It can save you an hour of recovery later.

  1. Have console access (VNC/KVM/serial from your provider). If you can’t get a console, schedule a maintenance window. If you can’t schedule one, use the rollback timer below.
  2. Confirm your SSH source IP (office VPN, home IP, or bastion). If your IP changes often, use a VPN egress or a dedicated jump host.

Install tooling (iptables + persistence)

On Ubuntu/Debian:

sudo apt update
sudo apt install -y iptables iptables-persistent netfilter-persistent

On AlmaLinux/Rocky (firewalld may be default; you can still use iptables directly, but be consistent):

sudo dnf install -y iptables iptables-services
sudo systemctl enable --now iptables

Note: In 2026, many distros use nftables under the hood. This tutorial sticks to iptables because it’s still common on hosting fleets and control-panel servers.

If your system uses the iptables-nft backend, these commands still work. They translate to nftables rules internally.

Snapshot your current rules

sudo iptables-save > ~/iptables.before.v4
sudo ip6tables-save > ~/iptables.before.v6
sudo iptables -S
sudo ip6tables -S

Set up a rollback timer (strongly recommended)

This restores your previous rules after 2 minutes unless you cancel it. Keep at least one SSH session open while you test.

sudo bash -c 'at now + 2 minutes <<EOF
iptables-restore < /root/iptables.before.v4 2>/dev/null || true
ip6tables-restore < /root/iptables.before.v6 2>/dev/null || true
EOF'

Copy the backups into /root so the rollback job can read them:

sudo cp ~/iptables.before.v4 /root/iptables.before.v4
sudo cp ~/iptables.before.v6 /root/iptables.before.v6

If you don’t have at installed:

sudo apt install -y at
sudo systemctl enable --now atd

iptables firewall tutorial: build a clean baseline ruleset

A readable ruleset is usually a simple one. Use a predictable pattern:

  • Start from a clean state (carefully).
  • Allow established traffic.
  • Allow loopback.
  • Allow only the ports you actually run.
  • Drop everything else.

Important: Do this from a stable SSH session. Don’t paste a port list into production unless you’re sure it matches what the server needs.

Step 1: define your variables

Pick your SSH port and (ideally) a trusted SSH source:

# Example values
SSH_PORT=22
TRUSTED_SSH_CIDR="203.0.113.10/32"

If you can’t allowlist a stable IP, skip TRUSTED_SSH_CIDR and allow SSH from anywhere. Then add Fail2Ban.

Step 2: flush existing rules (only if you’re ready)

sudo iptables -F
sudo iptables -X
sudo iptables -t nat -F
sudo iptables -t nat -X
sudo iptables -t mangle -F
sudo iptables -t mangle -X

For IPv6:

sudo ip6tables -F
sudo ip6tables -X

Step 3: set safe defaults

sudo iptables -P INPUT DROP
sudo iptables -P FORWARD DROP
sudo iptables -P OUTPUT ACCEPT

For IPv6:

sudo ip6tables -P INPUT DROP
sudo ip6tables -P FORWARD DROP
sudo ip6tables -P OUTPUT ACCEPT

Step 4: allow loopback and established connections

sudo iptables -A INPUT -i lo -j ACCEPT
sudo iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo ip6tables -A INPUT -i lo -j ACCEPT
sudo ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

Step 5: allow SSH (with or without allowlist)

Option A (recommended): SSH allowlist

sudo iptables -A INPUT -p tcp -s 203.0.113.10/32 --dport 22 -m conntrack --ctstate NEW -j ACCEPT

Option B: allow SSH from anywhere

sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT

Mirror the same for IPv6 if you use IPv6 SSH. If you allowlist, use your v6 CIDR:

sudo ip6tables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW -j ACCEPT

If you’re tightening SSH at the same time, follow our guide on secure SSH access with keys, allowlists, and audit logs. It pairs well with an iptables perimeter.

Step 6: allow web traffic (HTTP/HTTPS)

sudo iptables -A INPUT -p tcp --dport 80  -m conntrack --ctstate NEW -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -m conntrack --ctstate NEW -j ACCEPT

IPv6:

sudo ip6tables -A INPUT -p tcp --dport 80  -m conntrack --ctstate NEW -j ACCEPT
sudo ip6tables -A INPUT -p tcp --dport 443 -m conntrack --ctstate NEW -j ACCEPT

If you’re enabling QUIC/HTTP/3, you also need UDP 443. Our Nginx QUIC guide covers the web-server side: enable HTTP/3 (QUIC) on Nginx.

Add this firewall rule:

sudo iptables -A INPUT -p udp --dport 443 -j ACCEPT
sudo ip6tables -A INPUT -p udp --dport 443 -j ACCEPT

Step 7: allow essential ICMP (don’t break PMTU)

Blindly blocking ICMP is a great way to create “some pages load, some hang” tickets. Allow it and move on.

sudo iptables -A INPUT -p icmp -j ACCEPT

For IPv6, ICMPv6 is required for basic network health (neighbor discovery, PMTU). Allow it:

sudo ip6tables -A INPUT -p ipv6-icmp -j ACCEPT

Step 8: add basic bad-packet drops

These cut down on junk traffic. They won’t stop a real DDoS, and they don’t try to.

# Drop invalid states
sudo iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
sudo ip6tables -A INPUT -m conntrack --ctstate INVALID -j DROP

Step 9 (optional): rate-limit new SSH attempts

This won’t replace Fail2Ban. It can make bursty scans less annoying.

sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
  -m recent --set --name SSH

sudo iptables -A INPUT -p tcp --dport 22 -m conntrack --ctstate NEW \
  -m recent --update --seconds 60 --hitcount 10 --name SSH -j DROP

If you want application-aware bans (WordPress logins, XML-RPC, wp-admin), use Fail2Ban. It’s easier to live with long-term: Fail2Ban for SSH and WordPress brute-force protection.

Step 10: log a small sample of drops

Logging every dropped packet will fill disks and bury useful signals. Sample a little, and keep the prefix obvious.

sudo iptables -A INPUT -m limit --limit 6/min --limit-burst 20 \
  -j LOG --log-prefix "iptables-drop " --log-level 4

Your final drop is already handled by the default policy. If you prefer an explicit rule anyway:

sudo iptables -A INPUT -j DROP

Quick test: don’t guess

From your workstation, verify:

  • SSH: ssh -p 22 user@your_server_ip
  • HTTP: curl -I http://yourdomain
  • HTTPS: curl -I https://yourdomain

On the server, check counters to confirm packets are hitting the rules you expect:

sudo iptables -L -n -v --line-numbers

Make iptables rules persistent (Ubuntu/Debian and RHEL-family)

If you don’t persist rules, the next reboot can surprise you. Depending on service startup order, you might reopen services or block access.

Ubuntu/Debian with netfilter-persistent

sudo netfilter-persistent save
sudo systemctl enable --now netfilter-persistent

Rules save to:

  • /etc/iptables/rules.v4
  • /etc/iptables/rules.v6

AlmaLinux/Rocky with iptables-services

sudo service iptables save
sudo systemctl enable --now iptables

Rules typically save to /etc/sysconfig/iptables and /etc/sysconfig/ip6tables.

Hosting-specific port checklist (open only what you run)

Most web servers need only 22, 80, and 443. Panels and mail stacks expand that list fast. Keep it tight, especially on public interfaces.

  • cPanel/WHM: 2087 (WHM), 2083 (cPanel), 2086/2082 (non-SSL, usually avoid), 2078/2077 (WebDisk)
  • Plesk: 8443, 8880 (non-SSL, often avoid)
  • DirectAdmin: 2222
  • FTP: 21 + passive range (prefer SFTP instead)
  • Mail: 25 (SMTP), 465 (SMTPS), 587 (submission), 110/995 (POP3), 143/993 (IMAP)

Example: allow only WHM to your office IP, not the whole internet:

sudo iptables -A INPUT -p tcp -s 203.0.113.10/32 --dport 2087 -j ACCEPT

Running email on the same box? Before you open mail ports, make sure DNS is correct (SPF/DKIM/DMARC and rDNS).

Two practical references:

Common pitfalls (and fast fixes)

You locked yourself out

  • Use provider console access, then restore: iptables-restore < /root/iptables.before.v4
  • If the rollback timer is still pending, wait two minutes and reconnect.
  • Verify the SSH rule has the right port and the right source IP/CIDR.

HTTPS works but HTTP/3 doesn’t

If you enabled QUIC in Nginx, confirm UDP 443 is allowed. TCP 443 alone won’t carry HTTP/3.

Some clients hang or uploads stall

That’s often PMTU trouble. You probably blocked ICMP or ICMPv6. Re-allow it as shown above.

Your logs are filling disk

Lower the log rate or remove the LOG rule. You can also route firewall logs to a separate file via rsyslog. Start by reducing volume.

Verify from the outside: a quick “hosting reality check”

Run these from a separate machine (not from the server). Replace the IP.

# Basic port scan (only the ports you expect should be open)
nmap -Pn -p 22,80,443 YOUR.SERVER.IP

# If you run a panel
nmap -Pn -p 2087,2083 YOUR.SERVER.IP

If you manage multiple servers, write down each host’s exposed ports in your inventory. During an incident, that note saves time.

When iptables isn’t enough: pair it with monitoring and app-layer controls

iptables is perimeter control. It won’t tell you PHP-FPM is wedged, disk is full, or the kernel is dropping packets under load.

Add monitoring early so you see failures before users do.

Follow our guide to set up uptime checks, alerts, and resource tracking on Ubuntu. You’ll catch problems while they’re still small.

For WordPress-heavy boxes, you’ll usually get more benefit from web-server tuning than from piling on firewall rules. Keep this ruleset tight, then optimize Nginx/PHP: Nginx performance optimization for WordPress and PHP sites.

Summary: your production-ready baseline

  • Default DROP for inbound; allow only what you need.
  • Keep ESTABLISHED,RELATED and loopback allowed.
  • Allow SSH carefully (prefer allowlists + keys).
  • Open 80/443 (and UDP 443 if you run HTTP/3).
  • Allow ICMP/ICMPv6 to avoid PMTU headaches.
  • Persist rules and test from outside.

If you want a hosting platform where you can apply changes like this confidently, start with a HostMyCode VPS. If your priority is uptime and support rather than day-to-day maintenance, managed VPS hosting is the safer path for production websites.

Need a server that’s meant to be administered—not babysat? HostMyCode offers VPS plans for hands-on control and managed VPS hosting when you want the firewall, updates, and monitoring handled by a team.

FAQ

Should I use UFW or iptables on a hosting VPS?

Use UFW if you want a simpler interface and fewer moving parts. Use iptables if you need precise rule ordering, custom chains, or you’re matching an existing fleet. Either works if you keep the rules small and you test them.

Do I need separate rules for IPv6?

Yes. If your server has IPv6, leaving it unfiltered can expose services you assumed were blocked. Mirror your IPv4 intent in ip6tables and allow ICMPv6.

Will iptables stop DDoS attacks?

It helps with low-level noise (invalid packets, small scans). Large volumetric DDoS requires upstream mitigation and capacity planning. Keep iptables simple and avoid expensive rules that burn CPU under load.

What’s the safest way to apply changes without getting locked out?

Keep an SSH session open, schedule a rollback timer, then apply rules in small steps. Test connectivity after each step, and only persist once you’ve verified from the outside.

What ports should I open for a typical WordPress VPS?

Usually 22 (SSH), 80 (HTTP), and 443 (HTTPS). Add UDP 443 if you run HTTP/3. Avoid exposing database ports publicly, and prefer SFTP over FTP.

IPTables Firewall Tutorial (2026): Build a Production Web-Hosting Ruleset for a VPS or Dedicated Server | HostMyCode