A default-deny iptables firewall needs an early rule for traffic that belongs to connections the host already started or explicitly allowed. Without that stateful allow rule, replies can be dropped by a later DROP policy even when outbound traffic and allowed service ports look correct.

The conntrack match reads kernel connection-tracking state with --ctstate. ESTABLISHED matches packets for connections that have seen traffic in both directions, while RELATED covers dependent traffic such as selected ICMP errors or helper-managed data connections.

Place the rule before broad DROP or REJECT rules in the INPUT chain. Check for an existing rule before inserting a duplicate, repeat the equivalent rule with ip6tables when the host accepts IPv6, and save the ruleset only after a separate connection test confirms management access still works.

Steps to allow established iptables connections:

  1. List the current INPUT policy and rule order.
    $ sudo iptables -S INPUT
    -P INPUT ACCEPT
    -A INPUT -i lo -j ACCEPT
    -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT

    Rule order matters. A broad DROP or REJECT rule above the state match can still block reply traffic before this rule is reached. Related: How to list iptables rules with counters

  2. Check whether the stateful allow rule already exists.
    $ sudo iptables -C INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
    iptables: Bad rule (does a matching rule exist in that chain?).

    No output with exit status 0 means the rule already exists. The error above means it needs to be inserted.

  3. Insert the conntrack rule at the top of the INPUT chain.
    $ sudo iptables -I INPUT 1 -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT

    iptables may display the state list as RELATED,ESTABLISHED after insertion. The order inside --ctstate does not change the match.

  4. Confirm the rule appears before service allow rules and drop rules.
    $ sudo iptables -S INPUT
    -P INPUT ACCEPT
    -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
    -A INPUT -i lo -j ACCEPT
    -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
  5. Apply the intended default-deny INPUT policy after required allow rules are visible.
    $ sudo iptables -P INPUT DROP

    Run this only from a console or an already-open remote session after loopback and management rules are present. Use sudo iptables -P INPUT ACCEPT to reopen the policy while correcting a failed test.

  6. Reset INPUT counters before a controlled connection test.
    $ sudo iptables -Z INPUT

    Counter reset is optional, but it makes the next verification output easier to read on a busy host.

  7. Open an outbound connection from the firewalled host.
    $ curl -sS http://peer.example.net:8080/
    iptables peer service

    Use a service you are allowed to reach from the host. The inbound reply packets should match the RELATED,ESTABLISHED rule.

  8. Check that the state rule counter increased.
    $ sudo iptables -L INPUT -n -v --line-numbers
    Chain INPUT (policy DROP 0 packets, 0 bytes)
    num   pkts bytes target     prot opt in     out     source               destination
    1        8   719 ACCEPT     all  --  *      *       0.0.0.0/0            0.0.0.0/0            ctstate RELATED,ESTABLISHED
    2        2   148 ACCEPT     all  --  lo     *       0.0.0.0/0            0.0.0.0/0

    The packet count on the RELATED,ESTABLISHED line confirms reply traffic is using the state match.

  9. Test a new inbound connection that should remain blocked by the firewall.
    $ nc -vz server.example.com 9090
    nc: connect to server.example.com port 9090 (tcp) failed: Connection timed out

    A blocked NEW connection confirms the state rule is not opening every inbound port. If the port should be reachable, add a separate service-specific allow rule before the drop policy.

  10. Review the final ruleset before saving it permanently.
    $ sudo iptables-save
    *filter
    :INPUT DROP [0:0]
    ##### snipped #####
    -A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
    -A INPUT -i lo -j ACCEPT
    -A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
    COMMIT

    Use the persistence method for the host after reviewing the output. Related: How to save iptables rules permanently