Are NACLs Stateful or Stateless? | Traffic Rules That Matter

No, AWS network ACLs are stateless filters, so return traffic needs its own matching inbound or outbound rule.

AWS network access control lists, often called NACLs, control traffic at the subnet layer in a VPC. They don’t track an open connection. Each packet is checked on its own, whether it’s entering the subnet or leaving it.

That one detail explains many broken AWS traffic flows. A web request may reach an instance on port 443, yet the reply can still fail if the outbound NACL rule blocks the client’s return port. Security groups behave differently, which is why mixing the two up causes so much confusion.

What Stateless Means For AWS NACL Traffic

Stateless means the NACL does not remember that traffic came in a few milliseconds earlier. It checks inbound rules for inbound packets and outbound rules for outbound packets as separate events.

AWS states that network ACLs control subnet traffic through inbound and outbound rules. The rule match happens before AWS decides whether to allow or deny the packet at that subnet boundary.

So, when a client connects to an EC2 instance, two directions matter:

  • The request entering the subnet must match an inbound allow rule.
  • The reply leaving the subnet must match an outbound allow rule.
  • If either side lacks a match, the flow breaks.

This is not a bug. It’s the design. NACLs work well as broad subnet guardrails because they apply to every resource in the linked subnet. They’re less forgiving than security groups because they do not auto-allow reply traffic.

NACL Stateful Versus Stateless Rules In Real Subnets

Security groups are stateful. If a security group allows inbound HTTPS to an instance, the response can go back out without a separate outbound rule for that same connection. NACLs don’t work that way.

AWS explains this difference in its security group and network ACL connection guidance: security groups are stateful, while network ACLs require both inbound and outbound permissions. That line is the cleanest way to remember the split.

Here’s the plain traffic story. A laptop opens a web page hosted on an EC2 instance. The laptop sends traffic from a random high-numbered source port to port 443 on the server. The server replies from port 443 back to that random high-numbered client port.

For the NACL to pass that traffic, the inbound side must allow TCP 443 from the client range. The outbound side must allow the return traffic to the client’s ephemeral port range. If outbound only allows TCP 443, the reply won’t pass.

Why Ephemeral Ports Matter

Ephemeral ports are temporary ports used by clients during a connection. They are often high-numbered ports, and the exact range can vary by operating system or AWS service. This matters because return traffic often targets those temporary ports.

AWS notes in its custom network ACL rules page that for every rule you add, there must be an inbound or outbound rule that allows response traffic. That’s the stateless rule in action.

The safest habit is to diagram each flow in both directions. Write the source, destination, protocol, and port for the request. Then write the same four details for the response. That small step catches most NACL mistakes before they hit production.

Traffic Case Inbound NACL Need Outbound NACL Need
Public HTTPS To Web Server Allow TCP 443 from client IP range Allow TCP ephemeral ports back to clients
SSH From Admin IP Allow TCP 22 from admin IP only Allow TCP ephemeral ports back to admin IP
Instance Downloads Updates Allow TCP ephemeral ports from update endpoints Allow TCP 80 or 443 to update endpoints
Database From App Subnet Allow database port from app subnet CIDR Allow TCP ephemeral ports back to app subnet CIDR
DNS Query To Resolver Allow UDP or TCP ephemeral reply traffic Allow UDP or TCP 53 to resolver IP
Health Check To Load Balancer Target Allow health check port from load balancer subnet ranges Allow reply traffic to health check source ports
Blocked Bad CIDR Add lower-numbered deny rule for that CIDR Add matching deny if return or outbound traffic must stop too

How Rule Order Changes The Result

NACL rules are numbered. AWS checks the lowest number first and stops when a packet matches. A deny at rule 100 can block traffic before a later allow at rule 200 gets a chance.

This is one reason NACLs feel strict. Security groups gather all matching allow rules. NACLs work like a numbered gate list. The first match wins, then evaluation ends.

Use spacing in rule numbers so you can insert fixes later. Many teams start with 100, 110, 120, or wider gaps. That makes future changes cleaner than numbering every rule back-to-back.

Default NACL Versus Custom NACL

The default NACL in a VPC allows inbound and outbound IPv4 traffic unless changed. A custom NACL starts more locked down. If you create one and attach it to a subnet, you must add the allow rules your traffic needs.

A subnet can be linked to one NACL at a time. One NACL can be linked to more than one subnet. That makes NACLs useful for subnet-wide rules, but risky if you change a shared NACL without checking every subnet using it.

Common Mistakes That Break NACL Traffic

Most NACL failures are not strange AWS errors. They’re plain rule mismatches. The packet arrives, no matching allow rule exists, and the packet is denied by the final catch-all rule.

Watch for these patterns:

  • Allowing inbound port 443 but blocking outbound ephemeral ports.
  • Opening outbound 443 for a server reply when the destination is a client ephemeral port.
  • Adding a deny rule with a lower number than a needed allow rule.
  • Changing a shared NACL and breaking another subnet.
  • Forgetting IPv6 rules when the subnet uses IPv6 traffic.

VPC Flow Logs can help confirm the failure. If the log shows REJECT for the subnet traffic path, check NACL order, direction, protocol, CIDR, and port range before changing application settings.

Check Good Pattern Bad Pattern
Direction Request and reply both have rules Only inbound or only outbound is set
Rule Number Deny rules sit where intended A low deny blocks a later allow
Ports Ephemeral ports are allowed for replies Only service ports are allowed
Scope CIDR ranges match the real traffic path Rules use the wrong subnet or client range
Shared Use All linked subnets are checked before edits One subnet fix breaks another subnet

When To Use NACLs And Security Groups Together

Use security groups for resource-level access. They attach to EC2 instances, load balancers, databases, and many other AWS resources. Their stateful behavior makes normal application traffic easier to manage.

Use NACLs for subnet-level boundaries. They’re handy when you want a broad allow or deny line around a subnet. They can also deny traffic, which security groups cannot do.

A clean setup often uses both:

  • Security groups allow app-specific traffic at the resource layer.
  • NACLs set coarse subnet boundaries.
  • Route tables decide where traffic can go next.
  • Flow logs help verify what AWS accepted or rejected.

A Simple Rule Writing Method

Start with the traffic goal, not the AWS screen. Write a sentence like, “Admins from this office CIDR can use SSH to bastion hosts.” Then turn that into inbound and outbound NACL rules.

Next, add the security group rule for the instance. Test from the real source. If it fails, read the flow log, then adjust the narrowest wrong rule. Don’t widen all ports just to make the test pass.

Final Checks Before You Save NACL Rules

Before saving, walk through the packet path. Check the source CIDR, destination CIDR, protocol, port, direction, and rule number. Then repeat that pass for return traffic.

For the question “Are NACLs Stateful or Stateless?”, the answer is stateless. That means no connection memory, no automatic reply allowance, and no shortcut around outbound and inbound rule planning.

Once that clicks, NACLs become easier to reason about. Treat every packet as a new inspection. Give the request and the reply their own allowed path, and your subnet rules will behave the way you expect.

References & Sources