VPC peering has no reverse path forwarding, and AWS documents what that costs
A hub peered to two VPCs that share a CIDR will deliver one of them's replies to the other. AWS states the behaviour outright, and the configuration is legal to build because the overlap guardrail is narrower than people think.
· Venerable Networks
AWS refuses to create a peering connection between two VPCs with overlapping CIDRs. That refusal teaches people something slightly wrong: that AWS prevents overlap.
It prevents overlap within a single peering connection. A hub VPC peered to fifty spokes will happily accept two of those spokes being identical, because neither overlaps the hub. The guardrail is real and it is narrower than the conclusion drawn from it.
Here is what happens when you cross that line, in AWS's own words from the peering routing documentation:
AWS does not support unicast reverse path forwarding in VPC peering connections that checks the source IP of packets and routes reply packets back to the source.
The failure
Hub on 10.70.0.0/16. Two team VPCs, both on 10.71.0.0/16, because both teams adopted the same VPC module and neither overrode the default CIDR. The hub peers with both. Both peerings are legal, both go active.
The hub needs a route to 10.71.0.0/16. A route table holds exactly one route per destination prefix, so it gets exactly one, pointing at one of the two peerings.
Now a client in team B connects to a service in the hub. Team B's route table sends the request over its own peering, and it arrives. The hub's service answers. The reply is routed by destination address: the hub looks up 10.71.1.100, finds its single route for that prefix, and sends the packet to team C.
If team C has a host at that address — and if both teams used the same subnet layout, it probably does — that host receives a SYN-ACK for a connection it never opened and answers with a RST.
Team B's client sees a timeout. The hub team runs tcpdump, sees the SYN arrive and the SYN-ACK leave, and reasonably concludes the network is fine.
Why no route change fixes it
Every option fails for the same underlying reason, and working through them is worth more than the fix.
Repoint the route at team B. Now team B works and team C times out. The failure moves.
Add a second route for 10.71.0.0/16 via the other peering. A route table cannot hold two routes for one destination. The API rejects it.
Use longest prefix match — route 10.71.1.0/24 to one and 10.71.0.0/16 to the other. This is the cleverest wrong answer. It fails because both teams also used the same subnet layout. It is a legitimate documented technique for VPCs with multiple non-overlapping CIDR blocks; it just needs the specific ranges to differ.
Filter by source with a NACL. NACLs do not route. You can turn a wrong delivery into a clean drop, which is marginally safer and equally broken.
The problem is not the route table's contents. The hub is being asked to distinguish two networks that are, from an addressing standpoint, the same network. No routing construct can do that, because routing decides on destination address and the destination addresses are identical.
Transit Gateway does not rescue you either, for the same reason: a TGW route table also holds one route per prefix, so two attachments advertising 10.71.0.0/16 cannot both be reachable.
The two fixes that work
Re-address one side. The correct fix and the expensive one. A VPC CIDR cannot be changed after creation — only added to — so this means recreating the VPC and migrating everything in it.
Put a translation layer in the path. A private NAT gateway in the team VPC performs source NAT into a routable, non-overlapping range, so the hub sees a unique address per team and routes replies correctly. AWS documents this pattern for overlapping networks, with a load balancer handling the destination side. It costs a NAT gateway per team and it is the standard answer when re-addressing is off the table.
The organisational lesson is the real one
A default CIDR in a shared VPC module is a latent outage with a long fuse. Nothing breaks while teams are isolated. The bill arrives the day two of them need the same hub, and by then both VPCs are full of running workloads.
Make the CIDR a required variable with no default, and allocate from a registry. AWS IPAM exists for this and costs a rounding error next to one of these incidents.
Detecting it
The audit query is cheap: list every VPC CIDR in the organisation and look for duplicates.
aws ec2 describe-vpcs --query 'Vpcs[].[VpcId,CidrBlock]' --output text \
| sort -k2 | awk '{print $2}' | uniq -dAny prefix that appears twice is a future incident if those VPCs ever need to reach a common third party.
There is also a signal worth building a detection on. In this failure, team C's flow log records inbound accepted traffic from the hub for a connection nobody in team C initiated. That record is the anomaly, and it appears in a log belonging to a team not involved in the incident. A rule for "inbound accepted traffic on a peering with no preceding outbound flow" catches the general case.
If you want to watch a reply land in the wrong VPC, there is a lab: the reply that went to the wrong VPC.
The wider point is that AWS routing constructs decide on destination address, and any design needing the return path to depend on where a request came from is fighting the model. Transit Gateway's appliance mode exists precisely because the default cannot keep both directions of a flow on the same appliance — an explicit, opt-in mechanism for a property people assume they already have.