Network ACLs (NACLs)

A Network ACL is a stateless subnet-level firewall. Unlike security groups (stateful, instance-level), NACLs are evaluated by rule number in order, and you must explicitly allow both directions of traffic.

Key Differences from Security Groups

PropertySecurity GroupNetwork ACL
ScopeInstance-level (ENI)Subnet-level
StatefulYes — return traffic auto-allowedNo — must explicitly allow return
Default rulesAllow all outbound, deny all inboundAllow all inbound and outbound
Rule evaluationAll rules, most permissive winsBy rule number (lowest first, first match)
Explicit denySupportedSupported
Use casePrimary firewallSubnet-level explicit deny

Rule Structure

Rule #:100           ← evaluated in order (100, 200, 300...)
Type:    ALL Traffic   ← or specific protocol (TCP, UDP, ICMP)
Source:  10.0.0.0/16  ← CIDR or prefix list
Action:  ALLOW/DENY

AWS reserves rule numbers 100 (first rule) and 32766 (last rule, catch-all). You can use even numbers (100, 200, 300) to leave room for inserts.

Default NACL

The default NACL for a VPC allows all traffic, inbound and outbound. When you create a custom NACL, it denies all traffic by default.

Common NACL Configuration

Inbound:
┌──────┬────────────┬─────────────┬────────┬────────┐
│ Rule │ Source     │ Protocol    │ Action │ Notes  │
├──────┼────────────┼─────────────┼────────┼────────┤
│ 100  │ 0.0.0.0/0  │ TCP 443 │ ALLOW  │ HTTPS  │
│ 110  │ 0.0.0.0/0  │ TCP 80      │ ALLOW  │ HTTP   │
│ 120  │ 10.0.0.0/8 │ TCP22      │ ALLOW  │ SSH    │
│ 200  │ 0.0.0.0/0  │ ALL │ DENY   │ Block │
└──────┴────────────┴─────────────┴────────┴────────┘

Outbound:
┌──────┬────────────┬─────────────┬────────┬────────┐
│ Rule │ Dest │ Protocol    │ Action │ Notes  │
├──────┼────────────┼─────────────┼────────┼────────┤
│ 100  │ 0.0.0.0/0  │ TCP 443     │ ALLOW  │ HTTPS  │
│ 100  │ 0.0.0.0/0  │ TCP 80      │ ALLOW  │ HTTP   │
│ 100  │ 0.0.0.0/0  │ UDP 53 │ ALLOW  │ DNS    │
│ 200  │ 0.0.0.0/0  │ ALL         │ DENY   │ Block  │
└──────┴────────────┴─────────────┴────────┴────────┘

When to Use NACLs vs Security Groups

Use NACLs for:

  • Explicit deny of specific IP ranges at the subnet boundary (e.g., deny known malicious IPs)
  • Subnet-level policies that apply to all instances in the subnet regardless of their security group
  • Compliance requirements for explicit deny at network layer
  • Controlling traffic between tiers at the subnet level

Use Security Groups for:

  • Primary firewall for most workloads
  • Instance-level access control
  • Stateful connection tracking (automatic return traffic)

Best practice: Use both. Security groups as the primary firewall, NACLs as the explicit deny layer for specific threats.

Common Pattern: Public Subnet NACL

Public Subnet (ALB-facing):
Inbound:  Allow 0.0.0.0/0 → TCP 443 (HTTPS)
          Allow 0.0.0.0/0 → TCP 80  (HTTP, redirect to HTTPS)
Outbound: Allow 0.0.0.0/0 → TCP 443 (HTTPS outbound to internet)
          Allow 0.0.0.0/0 → TCP 80  (HTTP outbound)

Limits

ResourceLimit
NACLs per VPC200
Rules per NACL20 inbound + 20 outbound
Subnets per NACL1 (but one NACL can be attached to many subnets)

References

Pricing Examples

NACLs are free. The cost is in the EC2 or other resources using the subnets they protect.

Scenario 1: An NACL blocking a known malicious IP range (e.g., a botnet C2 server) at the subnet level before it reaches any instance. This saves compute resources (CPU cycles on each instance) that would otherwise process blocked traffic. At10,000 blocked connections/day × 10 instances, that’s significant CPU savings.

Scenario 2: A compliance requirement to log all network traffic at the subnet boundary. NACLs with a rule to DENY and log all traffic not explicitly allowed provides a subnet-level audit trail. CloudWatch Logs ingestion for NACL deny events: ~0.01/day = $0.30/month.

Nuggets & Gotchas

  • NACLs are stateless — you must explicitly allow return traffic: If you allow inbound TCP 80 from 0.0.0.0/0, you also need an outbound rule allowing TCP 80 response traffic. Without it, the established connection reply is blocked, and your instance can’t respond to HTTP requests.
  • NACL rule numbers are evaluated in order — first match wins: Rule 100 is evaluated before rule 200. If you have an ALLOW rule at 100 and a DENY rule at 200 for the same traffic, the ALLOW wins. Use low numbers for allow rules and high numbers for deny rules.
  • NACLs apply to all instances in a subnet: If you attach an NACL to a subnet with 50 instances, the NACL rules apply to all 50 instances. This is powerful but dangerous — a misconfigured NACL affects every instance in the subnet.
  • The default NACL allows everything: The moment you create a custom NACL, it denies everything by default. If you attach it to a subnet without adding allow rules, all connectivity is cut off. Always configure the NACL before attaching it.
  • NACLs don’t filter traffic between instances in the same subnet: NACLs are evaluated at the subnet boundary, not per-instance. Traffic between two instances in the same subnet doesn’t cross the subnet boundary, so NACLs don’t apply. Use security groups for instance-to-instance filtering.