Understanding UFW Firewall Rule Ordering: Best Practices for SSH, HTTP/HTTPS Access Control


2 views

UFW (Uncomplicated Firewall) processes rules in sequential order, similar to iptables. The first matching rule wins, which means rule ordering is absolutely critical for proper firewall functionality. Unlike some other firewall systems, UFW doesn't automatically reorder rules for optimization.

When you set ufw default deny, it creates an implicit deny-all rule at the end of the chain. This doesn't appear in the rule listing but is always present. You can verify this by checking the iptables chain:

sudo iptables -L | grep -A 2 "ufw-user-input"

You'll see a final rule like DROP all -- anywhere anywhere.

Your current rule ordering is correct for most use cases:

  1. Specific allow rules (your internal IPs)
  2. General allow rules (HTTP/HTTPS)
  3. Special-case allows (deployment system)
  4. Implicit deny (from default policy)

To insert a rule at a specific position (before the implicit deny), use:

sudo ufw insert 1 deny from 123.45.67.89

For complex setups, I recommend maintaining a script to rebuild rules consistently:

#!/bin/bash
ufw reset
ufw default deny incoming
ufw allow from 217.22.12.111 to any port 22
ufw allow from 146.200.200.200 to any port 22
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow from 109.104.109.0/26 to any port 22
ufw enable

Always verify your rule ordering with:

sudo ufw status numbered

And test connectivity from both allowed and blocked IPs to confirm the firewall behaves as expected.

For production environments, consider:

  • Rate limiting SSH connections with ufw limit 22/tcp
  • Creating application profiles for complex services
  • Using ufw show added to see your custom rules separately

Remember that UFW operates on top of iptables/nftables. For extremely high-performance requirements, you might need to work directly with those subsystems.


UFW (Uncomplicated Firewall) processes rules in the order they appear in the configuration. The first matching rule wins, similar to iptables. This is crucial to understand when implementing firewall policies.

# Current rules listing:
sudo ufw status numbered
Status: active

     To                         Action      From
     --                         ------      ----
[1] 22                         ALLOW       217.22.12.111
[2] 22                         ALLOW       146.200.200.200
[3] 80                         ALLOW       Anywhere
[4] 443                        ALLOW       Anywhere
[5] 22/tcp                     ALLOW       109.104.109.0/26
[6] Anywhere                   DENY        Anywhere           (default)

The default deny rule doesn't show in the regular status output because it's a policy, not a specific rule. To see it, use:

sudo ufw status verbose

For your specific case, the current order makes sense:

  1. Specific SSH allow rules first (internal IPs)
  2. Web service rules next
  3. Deployment system SSH rule

When adding new rules that need higher priority, use the numbered insertion feature:

# Insert a new rule at position 3
sudo ufw insert 3 allow from 192.168.1.100 to any port 22

Always verify rules work as intended. For SSH specifically:

# From a test machine in the restricted network:
ssh -v user@yourserver.com
# Should succeed from allowed IPs and fail from others

For larger rule sets, consider using application profiles or organizing rules in a script:

#!/bin/bash
ufw reset
ufw default deny incoming
ufw allow from 217.22.12.111 to any port 22
ufw allow from 146.200.200.200 to any port 22
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow from 109.104.109.0/26 to any port 22
ufw enable
  • Always keep an active SSH connection when testing new rules
  • Consider rate limiting for SSH: sudo ufw limit 22/tcp
  • Regularly review and prune old rules