Security Groups: Stateful, Instance-Level Firewalls
A security group acts as a virtual firewall attached to individual ENIs (elastic network interfaces), typically associated with EC2 instances, RDS databases, or Lambda functions in a VPC, and it only supports 'allow' rules — there is no way to explicitly deny traffic with a security group. Critically, security groups are stateful, meaning that if you allow inbound traffic on port 443, the corresponding outbound response traffic is automatically permitted regardless of your outbound rules, so you rarely need to write matching return-traffic rules by hand. Rules can reference not just CIDR blocks but also other security groups by ID, which is the recommended pattern for tiered architectures — for example, allowing your database security group to accept traffic only from your application tier's security group rather than from a hardcoded IP range.
Cricket analogy: A security group is like a stadium's individual player-access wristband system — each player's wristband is checked at every gate they approach, and once verified, they can freely exit and re-enter without re-checking, just as stateful return traffic is auto-permitted.
Network ACLs: Stateless, Subnet-Level Firewalls
A Network ACL (NACL) operates at the subnet level rather than the instance level, evaluating both inbound and outbound rules independently and statelessly — meaning a NACL has no memory of prior traffic, so if you allow inbound traffic on port 443, you must also explicitly add an outbound rule allowing the ephemeral port range (typically 1024-65535) for the response to leave, or the connection will silently fail. Unlike security groups, NACLs support explicit deny rules and evaluate numbered rules in ascending order, stopping at the first match, which makes them useful for quickly blocking a known malicious IP range across an entire subnet without touching individual instance configurations. The default NACL that ships with every VPC allows all inbound and outbound traffic, while a custom NACL denies everything by default until you add rules.
Cricket analogy: A NACL is like a ground's perimeter security checking every vehicle entering and every vehicle leaving independently, with no memory between the two — a delivery truck allowed in during the morning must separately be cleared to exit in the evening.
Choosing the Right Layer for Defense in Depth
Most production architectures use both layers together rather than choosing one: security groups handle the fine-grained, stateful application-level rules (e.g., 'only the app tier can reach the database on port 5432'), while NACLs provide a coarser subnet-wide safety net, commonly used to explicitly deny traffic from a known bad IP range or to enforce a hard boundary between subnet tiers regardless of individual instance misconfiguration. Because NACL rules are evaluated in strict numeric order and stop at the first match, teams typically number rules in increments of 10 or 100 to leave room for inserting new rules later without renumbering the entire list, and it's a common best practice to leave the default NACL's permissive rules untouched while creating dedicated custom NACLs per subnet tier.
Cricket analogy: Combining both layers is like a team using both a strict fielding plan (security groups, fine-grained per-batsman) and a general ground-level boundary rule (NACL) that applies no matter which bowler is on — two independent layers of control working together.
# Security group: allow inbound HTTPS only from the app-tier security group
aws ec2 authorize-security-group-ingress \
--group-id sg-0dbtier \
--protocol tcp --port 5432 \
--source-group sg-0apptier
# NACL: explicitly deny inbound traffic from a known malicious CIDR range
aws ec2 create-network-acl-entry \
--network-acl-id acl-0123456789abcdef0 \
--rule-number 100 \
--protocol -1 \
--rule-action deny \
--egress false \
--cidr-block 203.0.113.0/24
# NACL: allow the ephemeral port range for stateless return traffic
aws ec2 create-network-acl-entry \
--network-acl-id acl-0123456789abcdef0 \
--rule-number 200 \
--protocol tcp \
--port-range From=1024,To=65535 \
--rule-action allow \
--egress true \
--cidr-block 0.0.0.0/0Because security groups only support allow rules, you cannot use one to block a specific bad actor's IP — that job belongs to a NACL, which supports explicit deny rules and applies them at the subnet boundary before traffic ever reaches an instance.
Forgetting to add an outbound NACL rule for the ephemeral port range (1024-65535) is one of the most common AWS networking mistakes — inbound requests will arrive successfully, but stateless NACLs will silently drop the response traffic, making the failure look like an application bug rather than a network misconfiguration.
- Security groups attach to ENIs, are stateful, and support only allow rules.
- NACLs attach to subnets, are stateless, and support both allow and deny rules.
- Stateful security groups auto-permit return traffic; stateless NACLs require explicit rules for both directions.
- NACL rules are evaluated in numeric order and stop at the first match.
- The default NACL allows all traffic; a custom NACL denies everything until rules are added.
- Referencing security groups by ID (not hardcoded CIDRs) is the recommended tiered-architecture pattern.
- Production designs typically layer both security groups and NACLs for defense in depth.
Practice what you learned
1. Which statement correctly distinguishes security groups from NACLs?
2. Why must a custom NACL typically include a rule allowing the 1024-65535 port range?
3. In what order does AWS evaluate NACL rules?
4. Can a security group rule reference another security group as its source instead of a CIDR block?
5. What is the default behavior of a custom (non-default) NACL when first created?
Was this page helpful?
You May Also Like
VPC Fundamentals
Learn how Amazon VPC creates an isolated, software-defined network inside AWS, including CIDR planning, default vs custom VPCs, and connectivity options.
Subnets, Route Tables, and Internet Gateways
Understand how subnets divide a VPC, how route tables direct traffic, and how Internet Gateways and NAT Gateways enable inbound and outbound connectivity.