Bali, Indonesia
Insights

Transit Gateway Shows the Route, but Traffic Still Drops? Check Effective Routes

November 4, 2026
0:00
A common assumption in cloud networking tickets goes like this: the transit gateway or hub dashboard shows a route to the destination network, so routing is fine, and the problem must be a firewall or the application. Tickets get closed as 'routing verified' while the customer's traffic keeps timing out. The hub route table is only one of several tables a packet consults on its way across, and it tells you nothing about the path the reply takes. This article covers why that assumption fails, what effective routes actually show, and how to confirm route symmetry on both ends before you escalate a transit issue.
Transit services like AWS Transit Gateway, Azure Virtual WAN, and Google Cloud's Network Connectivity Center act as centralized cloud routers in a hub-and-spoke design. Unlike plain VPC peering, which is non-transitive, they are built to carry traffic between spokes. They collect routes through BGP or their own control plane and distribute them. That makes the hub dashboard look like the single source of truth. It isn't. When an instance in spoke A sends a packet to an instance in spoke B, the packet hits at least two route tables before it reaches the destination, and the reply hits two more on the way back:
  1. The route table attached to the source subnet must send the destination CIDR to the transit attachment.
  2. The hub route table associated with spoke A's attachment must have a route to spoke B.
  3. The route table attached to the destination subnet must send the reply toward the transit attachment.
  4. The hub route table associated with spoke B's attachment must have a route back to spoke A.
The dashboard most people check usually shows only step 2, or a merged view of everything the hub learned. If step 1 is missing, the packet never leaves spoke A. If step 3 or 4 is missing, the packet arrives fine and the reply goes nowhere. From the client's side, all of these look the same: a connection that hangs and times out. One more detail trips people up. In AWS, the hub doesn't use one route table for everything. Each attachment is associated with one transit gateway route table, and routes are propagated into tables separately. So 'the route exists on the transit gateway' can be true for the table you looked at and false for the table spoke B's attachment actually uses. Azure vWAN and GCP NCC have their own association and propagation models, so the exact terms differ, but the lesson carries over. If the two spokes are connected by peering rather than a transit construct, that's a separate problem: peering won't carry traffic between two other networks no matter what the routes say. That case is covered in Peered Both VPCs to the Hub After Migration, Yet They Still Can't Reach Each Other.
The effective route table is the set of routes that actually applies to a specific subnet or network interface after every source has been merged: system routes, static routes, propagated BGP routes, and any user-defined overrides. Selection follows longest prefix match, meaning the most specific matching route wins. That's why a correct hub route can still lose. Say the hub advertises 10.20.0.0/16 for spoke B. If the source subnet also carries a more specific route such as 10.20.1.0/24 pointing at an old VPN, a NAT appliance, or a peering connection left over from a migration, traffic to 10.20.1.15 follows the /24 and never touches the hub. The hub dashboard stays green the whole time. Some other patterns show up often enough to check every time:
  • A subnet has no explicit route table association, so it quietly uses the VPC's main route table, which nobody updated.
  • A transit gateway route shows a blackhole state because the attachment it pointed at was deleted or recreated.
  • In Azure, a user-defined route sends a prefix to a next hop of None, or to a virtual appliance that no longer forwards.
  • In AWS, the VPC attachment has subnets in only some Availability Zones. Instances in an AZ without an attachment subnet can't reach the transit gateway, even though their subnet route table looks correct.
Check from the instance outward, once for the source and once for the destination. On AWS, start with the route table associated with each instance's subnet, then the transit gateway route table associated with each attachment.
BASH
# 1. Route table associated with the source subnet (replace IDs with your own)
aws ec2 describe-route-tables \
  --filters 'Name=association.subnet-id,Values=subnet-0aaa1111' \
  --query 'RouteTables[].Routes[]' --output table

# If that returns nothing, the subnet uses the VPC main route table
aws ec2 describe-route-tables \
  --filters 'Name=vpc-id,Values=vpc-0bbb2222' 'Name=association.main,Values=true' \
  --query 'RouteTables[].Routes[]' --output table

# 2. Which transit gateway route table this attachment is associated with
aws ec2 describe-transit-gateway-attachments \
  --transit-gateway-attachment-ids tgw-attach-0ccc3333 \
  --query 'TransitGatewayAttachments[].Association'

# 3. What that table does with the destination IP (longest prefix match)
aws ec2 search-transit-gateway-routes \
  --transit-gateway-route-table-id tgw-rtb-0ddd4444 \
  --filters 'Name=route-search.longest-prefix-match,Values=10.20.1.15/32'
Then repeat all three steps from the destination side, using the source IP as the lookup target. That second pass is the one that usually gets skipped. On Azure, the platform computes effective routes per network interface, which saves you from merging tables by hand. The VM has to be running for this to return results.
BASH
# Effective routes applied to a specific NIC, including UDRs and BGP-learned routes
az network nic show-effective-route-table \
  --resource-group rg-app --name vm-app-nic --output table
Look at the Source, State, and Next Hop Type columns for the destination prefix. An Invalid state means another route overrode that one. On GCP, gcloud compute routes list shows the VPC routes, and the console's route views or Connectivity Tests can help you confirm which route applies to a given instance. The exact tooling depends on how NCC is set up in your environment. Finally, check inside the guest OS. Instances with multiple network interfaces, leftover static routes, or VPN clients can send traffic out the wrong interface before the cloud fabric ever sees it.
BASH
# Run on the source instance, then on the destination with the source IP
ip route get 10.20.1.15
# Illustrative output format:
# 10.20.1.15 via 10.10.1.1 dev eth0 src 10.10.1.20 uid 0
If the output shows an unexpected dev or via, the problem is on the host, not the transit.
Route symmetry means the reply travels back over the same path, or at least through the same stateful devices, that the request used. It matters because security groups, cloud firewalls, and network virtual appliances track connections. If the request goes through firewall instance 1 and the reply comes back through firewall instance 2, instance 2 has no record of the connection and drops the reply. The usual cause is a central inspection VPC or hub with appliances in more than one Availability Zone, where each direction gets routed independently. On AWS, transit gateway appliance mode exists for this case. It keeps both directions of a flow on the same AZ path through the inspection attachment. On Azure vWAN, the equivalent concern is routing intent and how traffic is steered through the hub firewall. In either case, the symptom gives it away. Some connections work and others hang, often depending on which AZ the instances are in. Small requests may succeed while longer sessions die. A quick way to organize the check is to write down the next hop at each point in both directions:
CheckpointForward (A to B)Return (B to A)
Source or destination subnet tableB's CIDR goes to the transit attachmentA's CIDR goes to the transit attachment
Hub table for the sending attachmentRoute to B, not blackholeRoute to A, not blackhole
Inspection or appliance hop, if anyAppliance in AZ xSame appliance, same AZ
Guest OSCorrect interface and gatewayCorrect interface and gateway
If any cell in the return column is unknown, you haven't verified routing yet. For a closer look at telling one-way drops apart from a fully dead path, see Deciding If Inter-VPC Timeouts Are Asymmetric Drops or a Dead Path.
Once both directions check out, move from the control plane to the data plane. Enable VPC Flow Logs on both subnets and, on AWS, Transit Gateway Flow Logs on the attachments. If the source VPC logs show traffic leaving but the transit logs never show it, the problem sits between the subnet and the attachment. If the transit logs show the packet forwarded but the destination VPC logs don't show it arriving, look at the destination attachment and its subnets. If the request arrives and the reply never shows up in the return path's logs, you're back to symmetry or a stateful filter. After that come the usual non-routing suspects: security groups or NSGs on the destination, network ACLs, which are stateless and need explicit return rules for ephemeral ports, and host firewalls. In support work, this is where a ticket should get escalated. It should include the effective routes from both ends, the flow log evidence, and the specific hop where the packet disappears. A screenshot of the hub route table alone isn't enough. The practical rule is to treat a route in the hub dashboard as a claim about one hop. Before you call routing verified, pull effective routes for the source and the destination, walk the reply path as carefully as the request path, and confirm that stateful devices see both directions of a flow. That takes a few extra minutes, and it keeps transit tickets from closing as fixed while traffic is still dropping.