AWS VPCs Explained: The Real Difference Between Public/Private Subnets, IGWs, and NAT Gateways
A visual, no-fluff walkthrough of how AWS VPC routing actually works—why a subnet is public or private, what an Internet Gateway does, and when a NAT Gateway is the right tool.
Most AWS VPC conversations get confusing because they start with boxes instead of route tables. The real difference between a public subnet and a private subnet is not the name, the IP range, or the instances inside it. It is the route table.
The one rule that decides public vs. private
A subnet is public if its route table sends `0.0.0.0/0` to an Internet Gateway. A subnet is private if it does not have a direct route to an IGW.
That is the entire distinction for IPv4. According to the [AWS VPC User Guide](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html), a public subnet has a route to an IGW. A private subnet is every other subnet.
This is not a naming convention. A subnet with public IPv4 addresses but no IGW route cannot reach the internet. A subnet with no public IPs can still make outbound requests through a NAT Gateway. Reachability is defined by routing, not by whether the subnet looks private in a diagram.
What an Internet Gateway actually is
An Internet Gateway is a horizontally scaled, redundant AWS service attached to your VPC. It is not an EC2 instance, not a firewall, and not a NAT device in the traditional sense.
For IPv4, the IGW performs one-to-one network address translation between a public IPv4 address and the instance's private IPv4 address. The instance operating system only sees the private IP. For IPv6, the IGW passes traffic through directly.
To make a subnet public, you need two things:
1. An IGW attached to the VPC.
2. A route table associated with the subnet that has a default route: `0.0.0.0/0` targeting `igw-...`.
If you create a route table with a wildcard route to an IGW, every subnet associated with that route table is effectively public. That is why one shared route table across tiers is dangerous.
The mental model: front door vs. concierge
A VPC is an isolated building. Subnets are rooms in that building.
An Internet Gateway is the street-level front door. If a room has a hallway that leads to the front door, people can go out and visitors can come in—provided the door guard allows them.
A NAT Gateway is a concierge. People inside the building can place outgoing delivery orders through the concierge, and the concierge brings packages back. But outsiders cannot walk in through the concierge unannounced.
That asymmetry is the entire point of a private subnet. It allows outbound requests—package updates, API calls, dependency downloads—without accepting unsolicited inbound connections from the internet.
Visual routing map
Internet
|
Internet Gateway (IGW)
|
| public route table
| 10.0.0.0/16 -> local
| 0.0.0.0/0 -> igw-...
v
Public subnet 10.0.1.0/24
|
| NAT Gateway (with Elastic IP) is placed here
v
Private route table
10.0.0.0/16 -> local
0.0.0.0/0 -> nat-...
v
Private subnet 10.0.2.0/24The NAT Gateway must live in a public subnet because it needs its own route to the IGW. A private subnet then sends its internet-bound traffic to the NAT Gateway rather than directly to the IGW.
How the NAT Gateway changes the traffic flow
When an instance in a private subnet calls an external API:
1. The packet leaves the instance with a private source IP.
2. The route table sends the packet to the NAT Gateway in the public subnet.
3. The NAT Gateway replaces the source IP with its own Elastic IP address.
4. The response returns to the NAT Gateway, which maps it back to the original private IP.
This is source NAT. The external service never learns the private instance's address. More importantly, an external host cannot initiate a connection to the private instance through the NAT Gateway. There is no inbound mapping ready before the private instance starts the conversation.
Public vs. private vs. isolated
AWS production environments often use at least three tiers:
**Public tier**: Load balancers, bastions, NAT Gateways. Route to IGW.
**Private tier**: Application servers, workers, and internal services. Route to NAT Gateway for outbound dependencies.
**Isolated tier**: Databases, secrets stores, and compliance-heavy systems. No route to IGW and no route to NAT.
An isolated subnet cannot reach the internet at all. That is the strongest default position for systems that should only talk inside the VPC.
Cost and production notes
Internet Gateways are free. You do not pay an hourly fee for the IGW itself. NAT Gateways cost money: an hourly charge for the NAT Gateway plus a data processing charge for each gigabyte routed through it. There are also cross-AZ data transfer charges if private instances use a NAT Gateway in another Availability Zone.
NAT Gateways are Availability Zone-specific. For production, deploy one NAT Gateway per AZ and keep private route tables scoped to the same AZ. That prevents a single AZ outage from removing outbound internet access for healthy instances in another AZ.
Common mistakes
**Using one route table for public and private subnets.** The moment that route table contains a wildcard route to an IGW, every associated subnet becomes public.
**Putting a NAT Gateway in a private subnet.** It cannot reach the IGW, so it cannot reach the internet.
**Assuming a private subnet is secure because it is private.** Routing is one layer. Security groups, network ACLs, and application-level controls still matter.
**Treating an IGW as a firewall.** The IGW allows reachability. Security groups and NACLs decide what traffic is allowed.
The Sapior take
When we design AWS networks, we treat route tables as the source of truth. A diagram might say a subnet is private, but the route table always wins. If you cannot explain the route from `0.0.0.0/0` in one sentence, the network is still ambiguous.
The fix is not a more colorful diagram. It is three things: an attached IGW, a strict public route table, and a NAT Gateway per Availability Zone. After that, every subnet name starts to tell the truth.