Skip to content

Security groups and network ACLs

Security groups and network ACLs both filter traffic, but they behave differently in the one way that matters most when you write rules: security groups are stateful and network ACLs are stateless.

  • A security group tracks connections. Allow inbound TCP 443 and the return traffic is allowed automatically; there is no outbound rule to write.
  • A network ACL evaluates every packet independently, in rule-number order, with no memory of what came before. Every allowed inbound flow needs a matching outbound rule for its replies, and vice versa.

Worked example: exposing a web server on port 443

Section titled “Worked example: exposing a web server on port 443”

Security group

  • Inbound: allow TCP 443 from 0.0.0.0/0 (or, better, from the load balancer’s security group).
  • Outbound: nothing needed for the response — the group is stateful.

Network ACL

  • Inbound: allow TCP 443 from 0.0.0.0/0.
  • Outbound: allow TCP 1024-65535 to 0.0.0.0/0, for the ephemeral ports clients used as their source.

The ephemeral port is chosen by whatever initiated the connection, and different clients choose from different ranges:

ClientSource port range
Elastic Load Balancing1024-65535
NAT gateway1024-65535
AWS Lambda1024-65535
Many Linux kernels, including Amazon Linux32768-61000
Windows Server 2008 and later49152-65535

AWS’s guidance is to open 1024-65535 to cover the different types of client that might initiate traffic to a public-facing instance. Narrowing the outbound rule to the Linux kernel range (32768-61000 or 32768-65535) is a common mistake and a nasty one: it silently drops the return traffic for anything sourced by an ELB, a NAT gateway or a Lambda function, and the resulting failures are intermittent and hard to attribute.

If specific ports within that range must be blocked, add the deny rules with lower rule numbers than the wide allow rule. Network ACL rules are evaluated lowest-number-first and the first match wins, so a deny placed after the allow never fires.

Use security groups as the primary control — they are stateful, they reference other security groups by ID, and they express tier-to-tier intent directly. Use network ACLs as a coarse subnet-level backstop: a small number of broad rules, typically to deny a known-bad CIDR or to enforce a subnet-wide protocol restriction. A network ACL supports 20 rules by default, extendable to 40 inbound and 40 outbound, which is another reason to keep them coarse.