Checking firewall status in Linux shows whether incoming traffic is being filtered before it reaches listening services, which matters when validating hardening, troubleshooting an unreachable application, or confirming that a recent rule change took effect.
Linux packet filtering is handled by the kernel netfilter subsystem, but most hosts expose it through one frontend such as ufw, firewalld, direct nftables rules, or older iptables tooling. The clearest status check is to identify which service owns the rules, read that tool's runtime summary, and then compare the firewall view with the ports that are actually listening on the host.
Most status commands are read-only, but they often need sudo privileges to show the live ruleset or packet counters. Minimal containers may have the firewall packages installed without systemd running as PID 1, so systemctl checks fail there even though ufw, firewall-cmd, nft, or iptables can still inspect the backend. If more than one frontend is enabled on the same machine, treat that as a configuration problem until the intended rule owner is clear.
$ systemctl list-unit-files --type=service 'ufw*' 'firewalld*' 'nftables*' --no-pager UNIT FILE STATE PRESET firewalld.service enabled enabled nftables.service disabled enabled ufw.service enabled enabled $ systemctl is-active firewalld active
Use the active unit's matching CLI in the next steps. If systemctl reports that the system was not booted with systemd, skip directly to the tool-specific commands instead.
$ sudo ufw status verbose Status: active Logging: on (low) Default: deny (incoming), allow (outgoing), deny (routed) New profiles: skip To Action From -- ------ ---- 22/tcp ALLOW IN Anywhere 80/tcp ALLOW IN Anywhere 22/tcp (v6) ALLOW IN Anywhere (v6) 80/tcp (v6) ALLOW IN Anywhere (v6)
Status: inactive means ufw is installed but not currently enforcing rules.
$ sudo firewall-cmd --state running $ sudo firewall-cmd --get-active-zones public (default) $ sudo firewall-cmd --list-all public (default, active) target: default ingress-priority: 0 egress-priority: 0 icmp-block-inversion: no interfaces: sources: services: dhcpv6-client ssh ports: protocols: forward: yes masquerade: no forward-ports: source-ports: icmp-blocks: rich rules:
The services: and ports: lines show what the active zone is allowing at runtime. Empty interfaces: output is normal in some container-like environments or when the default zone is applied implicitly.
$ sudo nft list ruleset | sed -n '1,28p'
# Warning: table ip filter is managed by iptables-nft, do not touch!
# Warning: table ip6 filter is managed by iptables-nft, do not touch!
table ip filter {
chain ufw-before-logging-input {
}
chain ufw-before-logging-output {
}
chain ufw-before-logging-forward {
}
chain ufw-before-input {
iifname "lo" counter packets 0 bytes 0 accept
ct state related,established counter packets 0 bytes 0 accept
ct state invalid counter packets 0 bytes 0 jump ufw-logging-deny
ct state invalid counter packets 0 bytes 0 drop
ip protocol icmp icmp type destination-unreachable counter packets 0 bytes 0 accept
ip protocol icmp icmp type time-exceeded counter packets 0 bytes 0 accept
ip protocol icmp icmp type parameter-problem counter packets 0 bytes 0 accept
ip protocol icmp icmp type echo-request counter packets 0 bytes 0 accept
udp sport 67 udp dport 68 counter packets 0 bytes 0 accept
counter packets 0 bytes 0 jump ufw-not-local
ip daddr 224.0.0.251 udp dport 5353 counter packets 0 bytes 0 accept
If chain names begin with ufw- or the table name includes firewalld, a higher-level frontend is programming nftables beneath the surface.
$ sudo iptables -L -n -v | sed -n '1,20p'
Chain INPUT (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 ufw-before-logging-input 0 -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ufw-before-input 0 -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ufw-after-input 0 -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ufw-after-logging-input 0 -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ufw-reject-input 0 -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ufw-track-input 0 -- * * 0.0.0.0/0 0.0.0.0/0
Chain FORWARD (policy DROP 0 packets, 0 bytes)
pkts bytes target prot opt in out source destination
0 0 ufw-before-logging-forward 0 -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ufw-before-forward 0 -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ufw-after-forward 0 -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ufw-after-logging-forward 0 -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ufw-reject-forward 0 -- * * 0.0.0.0/0 0.0.0.0/0
0 0 ufw-track-forward 0 -- * * 0.0.0.0/0 0.0.0.0/0
Start by reading the chain policy and then the explicit ACCEPT, DROP, or REJECT targets. On modern distributions, iptables may still be showing rules from the nftables compatibility layer.
$ ss -tuln 'sport = :80' Netid State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess tcp LISTEN 0 1 0.0.0.0:80 0.0.0.0:*
A listening socket proves the service is bound locally, but it does not by itself prove that the firewall allows remote access.
$ nc -vz 127.0.0.1 80 Connection to 127.0.0.1 80 port [tcp/http] succeeded!
A loopback test only confirms local reachability. For a decisive inbound check, run the same probe from another host or network namespace that traverses the firewall path.