Security Groups Are Stateful, NACLs Are Stateless - The Design Decision Behind This Difference
Learn how Security Groups operate statefulness through connection tracking, why NACLs are stateless, the ephemeral port pitfall, and design patterns for defense in depth combining both.
The Difference Between Stateful and Stateless
A Security Group is a stateful firewall. Responses to requests allowed by inbound rules are automatically permitted regardless of outbound rules. Conversely, responses to requests allowed by outbound rules are automatically permitted regardless of inbound rules. A NACL (Network Access Control List) is a stateless firewall. Inbound and outbound rules are evaluated independently. Even if a response belongs to a request allowed by an inbound rule, it will be blocked if not explicitly allowed by an outbound rule. This difference comes from whether the implementation tracks the state of connections. Security Groups maintain a connection tracking table that records the state of each connection (source IP, destination IP, port, protocol). When a response packet arrives, it is matched against the connection tracking table, and if it belongs to an existing connection, it is automatically allowed. NACLs do not maintain a connection tracking table and evaluate each packet independently.
Why Two Types of Firewalls Exist
One reason to use NACLs together with Security Groups is the defense-in-depth principle. Security Groups operate at the instance level (ENI level), while NACLs operate at the subnet level. These two layers provide protection at different levels. If a Security Group is compromised (for example, if an attacker with IAM permissions modifies Security Group rules), the NACL serves as a backup. Since NACLs apply to the entire subnet, their rules remain applied at the subnet level even if individual instance Security Groups are altered. Another reason is the need for explicit Deny rules. Security Groups can only define allow rules, not deny rules. If you want to explicitly block access from a specific IP address, Security Groups cannot do this. NACLs can define both allow and deny rules, evaluated in order by rule number, enabling explicit blocking of specific IP addresses.
The Ephemeral Port Pitfall with NACLs
One misconfiguration caused by NACLs being stateless is failing to allow ephemeral ports (temporary ports). When a client connects to a server, the client's source port uses an ephemeral port dynamically assigned by the operating system. Linux uses the range 32768-60999, and Windows uses 49152-65535. Even if the NACL outbound rule allows HTTP (port 80), the response is sent back to the client's ephemeral port. If the NACL inbound rule does not allow the ephemeral port range, the response is blocked. This problem does not occur with Security Groups. Being stateful, responses to outbound-allowed requests are automatically permitted. When using NACLs, you configure both inbound and outbound rules to allow the ephemeral port range (1024-65535). If allowing such a wide range feels uncomfortable, one approach is to use Security Groups for fine-grained control and keep NACLs for coarse subnet-level control.
Security Group Connection Tracking in Detail
Security Group connection tracking has both tracked and untracked connections. For TCP and UDP flows, when a rule allows all traffic (0.0.0.0/0 or ::/0) and a rule in the other direction allows all response traffic for any port, connection tracking is not performed (except for automatically tracked connections) because it is unnecessary. The connection tracking table has a size limit. Amazon EC2 defines the maximum number of connections that can be tracked per instance. When the maximum is reached, packets that are sent or received are dropped because new connections cannot be established. For workloads handling large numbers of short-lived connections (such as instances behind a load balancer), you monitor the number of tracked connections. The conntrack_allowance_exceeded network performance metric shows whether packets were dropped because the maximum number of tracked connections was exceeded. The default idle connection tracking timeouts are 30 seconds for UDP and 180 seconds for UDP streams, and the timeout for established TCP connections varies by instance type. Timed-out connections are removed from the table.
Practical Defense-in-Depth Design Patterns
Here are practical design patterns combining Security Groups and NACLs. For an ALB in a public subnet, allow inbound HTTP/HTTPS (0.0.0.0/0) in the Security Group and leave the NACL at default (allow all). Since the ALB is a public-facing service, this configuration does not restrict it at the NACL level. For application servers in a private subnet, allow inbound traffic only from the ALB's Security Group in the Security Group, and allow inbound traffic only from the ALB subnet's CIDR in the NACL. The Security Group allows only ALB traffic, and the NACL provides backup defense at the subnet level. For the database subnet, allow inbound traffic only from the application server's Security Group in the Security Group, and allow inbound traffic only from the application subnet's CIDR in the NACL. If an attack from a specific IP address is detected, it can be blocked with a NACL deny rule. Changing a Security Group rule does not immediately interrupt tracked connections, but blocking traffic with a NACL also breaks existing connections, so NACLs can be used for emergency response.
References (Official AWS Resources)
The primary sources for this page are the official AWS website and documentation. Check the official pages below for the latest specifications and pricing.
If this page and the official documentation disagree, treat the official documentation as authoritative.