An Azure service that provides a platform-managed, scalable, and highly available application delivery controller as a service.
Resolution update: Application Gateway backend health never completed across an NVA/OpenVPN tunnel — fixed by moving the backend into the VNet
Update / resolution — thank you for the detailed checklist.
Posting the outcome in case it helps someone with the same symptom.
Background
The outage started after OS/package updates (and a restart) on our network virtual appliance (NVA). The NVA carried the only path from the Application Gateway to a backend origin that lived outside Azure, reached over an OpenVPN tunnel with SNAT applied. The update wiped the non-persistent Linux IP-forwarding and NAT/MASQUERADE state the path depended on, so all public hosts began returning 502 Bad Gateway while the origin itself stayed healthy (it answered correctly when reached directly).
We re-applied forwarding/NAT and also found and removed a route-table self-reference (a UDR loop where the same route table was associated with both the Application Gateway subnet and the NVA subnet). After that, effective routing showed healthy hops — but backend health still would not recover.
The persistent symptom
Packet captures proved the full path worked in both directions:
- The probe SYN reached the NVA, was forwarded and SNATed across the tunnel to the backend.
- The backend returned SYN-ACK.
- The NVA reverse-NATed that reply back toward the Application Gateway.
- …but the Gateway never sent the final ACK, so the handshake never completed. Backend health stayed Unhealthy and the public hosts kept returning 502.
What we validated (none of it restored backend health)
- NSG on the Application Gateway subnet allowed inbound from the GatewayManager service tag on the probe-management port range for our SKU.
- No
0.0.0.0/0default route on the Application Gateway subnet forced probe traffic through a packet-modifying next hop; the earlier UDR self-reference was removed. - Backend probe settings (protocol / port / expected status) matched the reverse proxy; the origin returned 200 directly and presented a valid wildcard certificate; TLS negotiation to the origin succeeded from a direct client.
- The backend was listening on the probed port and served correctly whenever it was reached directly, bypassing the Gateway.
Root cause (as far as we could establish)
The Application Gateway backend was outside the VNet, reachable only through an NVA + OpenVPN tunnel with SNAT. Even with captures proving packets traversed the whole path in both directions, the Gateway would not complete/accept the probe's TCP session across that asymmetric, SNATed, tunneled return path. We could not get backend health to go Healthy while the origin remained behind those hops, regardless of further NSG/UDR tuning — because the problem was not a block, it was the Gateway being unable to establish a usable TCP session over that path.
Resolution
We stopped trying to make the health check succeed across the tunnel and removed the cross-network hops entirely:
- Relocated the application stack onto an Azure VM in the same VNet as the Application Gateway.
- Repointed the backend pool at that VM's private IP (a native, in-VNet path — no NVA, no OpenVPN, no on-prem hop).
- Changed the API backend setting from HTTPS:8001 to HTTPS:443 so it flows through the same reverse proxy as the other hosts.
As soon as the backend was an Azure-local VM with a direct in-VNet path, all backend targets went Healthy and every public host returned HTTP/2 200 immediately.
Takeaway
If Application Gateway backend health is stuck Unhealthy even though captures show a complete SYN → SYN-ACK exchange, and your backend is only reachable across an NVA/VPN tunnel with SNAT outside the VNet, the fix may not be another NSG/UDR change — the probe's TCP session simply may not survive that asymmetric tunneled path. Giving the backend a native, symmetric Azure path (e.g. putting it in the same VNet as the Gateway) resolved it cleanly for us.
Thanks again for the help.