Updated: October 2026
The rules look right, the traffic still dies, and the logs say "no session" or "invalid state." That message comes from the part of a stateful firewall nobody writes rules for: the state table. Here's how a stateful firewall builds that table, what breaks it, and how to read it when a ticket lands.
What Is a Stateful Firewall?
A stateful firewall remembers connections. When a packet starts a new connection and a rule allows it, the firewall writes a row to its state table. Every later packet in that conversation is checked against the row, not the rule list. Replies get in because they match a row, so you only write the rule for the direction that starts the conversation.
Stateful packet inspection (SPI) and stateful inspection are two names for the same idea. The firewall inspects headers and connection state, not the content. That's the difference from a plain packet filter, which judges each packet alone and needs a rule for every direction. If you want the concept first, with apps and HTTP sessions alongside firewalls, the stateful vs stateless explainer covers it.
The payoff is fewer, tighter rules. Nothing on the internet can start a conversation with a laptop behind the firewall unless a rule says so, yet replies to the laptop's own requests come back without an inbound rule.
What Lives in the State Table
Each row describes one connection. The core is the 5-tuple: source IP, source port, destination IP, destination port and protocol. Next to it sit a state, a timeout, and on most edge firewalls the NAT translation, so the reply can be mapped back to the private address.
TCP gives the firewall real states to follow. A SYN creates the row in a SYN_SENT state. The handshake completes and the row moves to ESTABLISHED. FIN or RST packets move it through closing states until it's removed. A packet that doesn't fit the current state, like a stray ACK with no handshake behind it, gets dropped as invalid. Many firewalls also check that sequence numbers fall inside the expected window.
UDP and ICMP have no handshake, so the firewall invents one. Linux connection tracking marks a UDP flow as unreplied until an answer comes back, then as a stream once traffic flows both ways. An ICMP echo is matched on its identifier. ICMP errors about an existing flow, like "port unreachable," are let through as related traffic. Without the handshake, timers do all the cleanup: the Linux kernel defaults to 30 seconds for a one-way UDP flow and 120 seconds for a UDP stream.
Stateful Inspection vs Next-Generation Firewalls
A next-generation firewall (NGFW) is a stateful firewall with more layers stacked on top. The state table stays. On top of it the NGFW adds application identification, user identity, intrusion prevention, URL filtering and, if you turn it on, TLS inspection.
The order matters when you troubleshoot. A packet that fails the state check never reaches the app-ID or IPS engines, and on many platforms it never reaches the traffic log either. It shows up as a drop counter instead: show asp drop on a Cisco ASA, the global counters on Palo Alto. Check the session first, then the security profile.
| Check | Packet filter (ACL) | Stateful firewall | NGFW |
|---|---|---|---|
| IPs, ports, protocol | Yes | Yes | Yes |
| Connection state and replies | No | Yes | Yes |
| Which app is talking | No | No | Yes |
| Which user is behind it | No | No | Yes |
| Exploit and malware signatures | No | No | Yes, with IPS |
| Encrypted payload | No | No | Only with TLS inspection |
Stateless vs Stateful Firewall: When Stateless Still Wins
A stateless filter has no table to fill, so it can't run out of memory and doesn't care which path a reply takes. That makes it the right tool in three places. Router and switch ACLs that protect the device's own management plane. The edge in front of a stateful firewall, where you drop obvious junk before it costs a table entry. And high-throughput paths where traffic takes different routes each way on purpose.
The price is rules in both directions and wide ephemeral-port ranges for replies. Treat stateless ACLs as a coarse outer fence and keep the stateful firewall where users and servers live.
What Breaks a Stateful Firewall
Asymmetric routing. The firewall only knows about connections it saw start. If the SYN leaves through firewall A and the SYN-ACK comes back through firewall B, B has no row and drops the reply. This shows up after someone adds a second WAN link, a second data center, or BGP preferences that send return traffic a different way. Redundant network topology makes it easy to build by accident. The fix is routing: make both directions use the same firewall or HA pair. Cisco's ASA has a TCP state bypass for these flows, and Cisco's own docs warn it "weakens the security of your network." Keep it for narrow, known traffic.
Failover without session sync. An HA pair only fails over cleanly if the standby already has the state table. Without session sync, every connection dies when the active unit goes down. On FortiGate, for example, session pickup is a setting you enable, and UDP and other connectionless sessions need a second one. Miss it, and VoIP and RDP over UDP drop while web browsing survives. The same applies to SD-WAN link failover: a session NATed out one ISP can't continue out another with a different public IP.
A full table. Every row costs memory, and tables have a ceiling. A SYN flood, a worm, or one host running a network scan can fill it, and then new connections fail for everyone. Firewalls fight back with SYN cookies or a SYN proxy, limits on half-open connections, and per-host session caps. Set a per-source limit before you need one.
SIP ALG and Other Protocol Helpers
FTP in active mode and SIP both negotiate a second connection whose port or address travels inside the data, not the headers. A stateful firewall can't match that second connection without help, so it runs an application layer gateway (ALG) that reads the payload and opens the extra pinhole. For FTP this works. SIP ALG has a worse record: it rewrites SIP messages, and a bad rewrite gives you one-way audio, phones that drop registration, or calls that die at about 32 seconds.
Network engineers have been asking vendors to ship SIP ALG switched off, because it keeps coming back on with every new router. Modern phone systems handle NAT on their own with STUN, keepalives and SIP over TLS, which an ALG can't read anyway. If phones misbehave behind a new router, check SIP ALG before you open a ticket with the provider.
Stateful Firewalls in the Cloud
Azure network security groups and AWS security groups are both stateful. AWS network ACLs are stateless. Two AWS details catch people out.
First, not every security group flow is tracked. If a rule allows traffic from 0.0.0.0/0 and the opposite direction allows all responses on any port, AWS skips tracking for that flow. That flips the usual rule-change behavior. Remove a narrow SSH rule and the tracked session keeps running until it times out. Remove a wide-open SSH rule and the untracked session drops at once.
Second, tracking has limits. Each instance can track a fixed number of connections, and AWS publishes a conntrack_allowance_exceeded metric for when it runs out. Idle timeouts also changed by generation. The TCP established timeout defaults to 350 seconds on Nitro v6 instances and 432,000 seconds (five days) on other types, per AWS's EC2 docs. A database pool that idles for ten minutes on a Nitro v6 instance will see resets it never saw on older types. Send TCP keepalives more often than the timeout, or set TcpEstablishedTimeout when you launch.
How to Read the State Table
When traffic dies and the rules look right, look up the session before you touch a rule. The commands differ by platform:
- Linux (iptables or nftables):
conntrack -Llists entries,conntrack -Sshows drops and insert failures. - pfSense or OPNsense:
pfctl -sslists states,pfctl -sishows the current count andpfctl -smthe limit. - Cisco ASA:
show connfor sessions,show asp dropfor why packets died. - FortiGate:
diagnose sys session listwith a filter,get system session statusfor the count. - Palo Alto:
show session all filter source <ip>for sessions,show session infofor table use. - Windows Defender Firewall:
netsh wfp show statewrites the filtering state to an XML file.
Three questions settle most tickets. Does a session exist for the flow? If it doesn't, the first packet never passed or took another path. Is the state what you expect? A session stuck in SYN_SENT means no reply came back through this firewall: it went another way or never came. Is the table near its limit? Then the problem isn't the rule at all. Track the session count over time in your network management software, so a filling table raises an alert before it drops traffic.
The Short Version
A stateful firewall is only as good as its state table. Keep traffic symmetric, sync sessions across HA pairs, cap what one host can fill, and switch off ALGs you don't need. When something breaks, check the session before the rule, because the rule usually isn't the problem.

Aliaska Varieva
Head of Platform
Hi! I’m Aliaska, and I’ve been working as a software engineer (mostly Java + a bit Kotlin) for over 8 years now. I mostly spend my time building backend services, integrating systems, fixing bugs (the fun part 🙃), and making sure things don’t fall apart behind the scenes.
