Centralized inspection VPC
Centralized egress and traffic inspection
One inspection VPC behind Transit Gateway steers every spoke's egress and east-west traffic through a firewall before a shared NAT.
Distributed versus centralized inspection
AWS Network Firewall can be deployed in three shapes. In a distributed model a firewall lives inside each individual VPC, giving the smallest blast radius but multiplying the number of firewalls, NAT gateways, and route tables you operate. In a centralized model the firewall lives in one inspection VPC and every other VPC (a spoke) reaches it through AWS Transit Gateway. A combined model puts east-west and egress inspection in the center while leaving dedicated inbound internet access distributed. The centralized model is the one that scales to dozens of accounts because you configure the ruleset, logging, and NAT once instead of per VPC.
The inspection VPC is not a general-purpose VPC. Per Availability Zone it carries two dedicated subnets: a firewall endpoint subnet that holds the Network Firewall (or Gateway Load Balancer) endpoint, and a separate Transit Gateway attachment subnet where the TGW places its elastic network interface. AWS is explicit that the firewall subnet is reserved: Network Firewall cannot inspect traffic whose source or destination lives inside the firewall subnet, so you never run workloads there.
Two traffic classes flow through this hub. North-south (egress) traffic is a spoke workload reaching the internet or on-premises. East-west traffic is one spoke talking to another. Both are forced through the same inspection endpoint by routing, not by an agent on the instance, which is why the design is transparent to the workloads. The tradeoff you accept is that the inspection VPC and Transit Gateway become a shared choke point, so they must be sized, spread across AZs, and monitored as critical infrastructure.
The data path and route-table design
Routing does all the steering, and it lives in two places: the VPC route tables and the Transit Gateway route tables. In each spoke, the workload subnet has a default route (0.0.0.0/0) pointing at the Transit Gateway. The Transit Gateway then uses separate route tables to break symmetry: a spoke route table associated with the spoke attachments sends 0.0.0.0/0 to the inspection VPC attachment, while a separate inspection (firewall) route table associated with the inspection attachment carries the propagated spoke CIDRs so post-inspection traffic can be delivered back to the right spoke. Keeping spokes and the inspection VPC in different TGW route tables is what forces traffic into the firewall instead of letting the TGW shortcut spoke-to-spoke.
Inside the inspection VPC the route tables are per-subnet and per-AZ. The TGW attachment subnet in a given AZ has a route table whose default route points at the firewall endpoint in that same AZ, so traffic arriving from the TGW is handed to the local firewall. After inspection, a return route table sends traffic back toward the Transit Gateway (for east-west, so it can reach the destination spoke) or onward to the NAT gateway (for egress). To stop spokes from bypassing the firewall by talking to each other directly through the TGW, you can add blackhole routes for the spoke CIDRs in the spoke route table so the only permitted next hop is the inspection VPC.
For egress specifically, AWS calls out a common trap: the Transit Gateway attachment in the egress VPC must be placed in a private subnet, not the public NAT subnet. If the attachment sits in the public subnet, traffic is routed straight to the internet gateway, which then drops it because the spoke instances have no public IP. Putting the attachment in a private subnet routes traffic to the NAT gateway first, which rewrites the source to its Elastic IP before the internet gateway forwards it.
Appliance mode and flow symmetry
Stateful firewalls track connection state, so the forward and return packets of a single flow must hit the same appliance in the same AZ. Transit Gateway's default behavior fights this: when appliance mode is not enabled, the TGW tries to keep traffic in its originating Availability Zone. That means a response originating in AZ 2 of a spoke is delivered to the AZ 2 firewall, even though the original request was inspected by the AZ 1 firewall. The AZ 2 appliance has no state for that flow and drops the packet. This is the classic asymmetric-routing failure.
Enabling appliance mode on the inspection VPC's TGW attachment fixes it. With appliance mode on, the Transit Gateway uses a flow hash to select one network interface in the appliance VPC and pins the entire flow (both directions) to that same interface, so bidirectional traffic is routed symmetrically through the same AZ for the life of the flow. Enabling it removes the need for workarounds like source NAT on the appliance to drag return traffic back.
There are hard constraints worth memorizing. You must connect exactly one Transit Gateway to the appliance VPC: multiple TGWs do not share flow state, so stickiness is not guaranteed. Flow stickiness is only guaranteed when the source and destination traffic arrive at the inspection VPC from the same TGW attachment; traffic can drop if the inspection VPC receives a flow from a different gateway (for example, an internet gateway) and then hands it to the TGW attachment after inspection. Enabling appliance mode on an existing attachment can also shift its current AZ path, so treat it as a change with routing impact.
Network Firewall or a GWLB appliance fleet
You have two engines for the inspection VPC. AWS Network Firewall is the managed, stateful, rules-based option (Suricata-compatible) with nothing to patch or scale. A Gateway Load Balancer (GWLB) fronting a fleet of third-party appliances is the choice when you need a specific vendor's layer 7 IPS/IDS or deep packet inspection. GWLB is a bump-in-the-wire: it transparently passes all layer 3 traffic to the appliances and is invisible to the source and destination.
GWLB reaches its appliances by encapsulating the original IP packet in a GENEVE header and forwarding it over UDP port 6081, which lets every protocol and port be inspected without configuring per-port listeners; the GENEVE header does not count against the GWLB MTU. For stateful firewalls, GWLB uses the packet's 5-tuple (or 3-tuple) to pin a flow to a single appliance for its lifetime, the same stickiness principle appliance mode provides at the TGW layer. The appliances must support GENEVE (RFC 8926) to participate, and GWLB endpoints are powered by AWS PrivateLink.
There is also a newer shortcut. Transit Gateway now supports a network function attachment for AWS Network Firewall: you attach the firewall directly to the TGW, AWS provisions a service-managed buffer VPC with one GWLB endpoint per AZ and appliance mode already enabled, and you skip building and routing an inspection VPC entirely. It supports static routing only and does not support third-party firewalls, so the GWLB-plus-appliance pattern remains the path for vendor NGFWs. Remember Network Firewall's blind spots regardless of engine: it does not inspect VPC peering traffic, AWS Global Accelerator traffic, or the AmazonProvidedDNS traffic for EC2.
Why one shared NAT beats per-VPC NAT
A NAT gateway bills two ways: a fixed hourly charge for every NAT gateway you run, plus a per-gigabyte data-processing charge on everything that passes through it. In a distributed design where each of N spoke VPCs owns its own NAT gateway (and typically one per AZ for resilience), you pay the fixed hourly charge N times over, whether or not the VPC is sending much traffic. Centralizing egress collapses those fixed charges to a single set of NAT gateways in the egress VPC, one per AZ, shared by every spoke. The per-GB processing still applies to the aggregate traffic, but the multiplied per-gateway overhead disappears, which is the dominant saving as the number of VPCs grows.
Centralizing NAT also centralizes the Elastic IP footprint. All spoke egress leaves from a small, known set of NAT Elastic IPs, which is what makes SaaS allow-listing and partner firewall rules manageable; per-VPC NAT would scatter source IPs across the whole estate. The same VPC that terminates inspection is the natural place to land the NAT, so the flow becomes spoke to TGW to inspection endpoint to NAT gateway to internet gateway, with a single point to attach flow logs and egress controls.
The cost is not free of caveats. Centralizing routes all egress through the Transit Gateway, so TGW per-GB data-processing charges now apply to that traffic, and if you use GWLB, PrivateLink charges apply to the endpoints. For very high-throughput, cost-sensitive estates you weigh those hub charges against the NAT consolidation savings. Some third-party appliances that support source NAT and overlay (two-arm) routing can even perform the address translation themselves, letting you drop the NAT gateway entirely, though that depends on vendor support and should be confirmed before relying on it.
More diagrams
Sources
- AWS Network Firewall example architectures with routing
- Deployment models for AWS Network Firewall (Networking & Content Delivery blog)
- How AWS Transit Gateway works: appliance mode and the shared services VPC scenario
- Centralized inspection architecture with AWS Gateway Load Balancer and AWS Transit Gateway
- Using the NAT gateway and Gateway Load Balancer for centralized IPv4 egress (Multi-VPC whitepaper)
Practice what you just read