The symptom: a Traffic Manager profile shows Degraded, an endpoint receives no traffic, or a failover happened that nobody expected. The app often works when you open it in a browser. That doesn't clear it, because a browser follows redirects and sends a real host name. The probe does neither unless you configure it.
A Degraded endpoint is left out of DNS responses, so it gets no new traffic. The profile shows Degraded when at least one endpoint is Degraded, even if the others are serving normally.
Step 1: read the status of the profile and each endpoint
az network traffic-manager profile show -g rg-prod -n tm-prod \
--query "{status:monitorConfig.profileMonitorStatus, protocol:monitorConfig.protocol, port:monitorConfig.port, path:monitorConfig.path, interval:monitorConfig.intervalInSeconds, timeout:monitorConfig.timeoutInSeconds, failures:monitorConfig.toleratedNumberOfFailures, ranges:monitorConfig.expectedStatusCodeRanges, ttl:dnsConfig.ttl}" \
-o json
az network traffic-manager endpoint list -g rg-prod --profile-name tm-prod \
--query "[].{name:name, target:target, admin:endpointStatus, monitor:endpointMonitorStatus, priority:priority}" \
-o table
| Monitor status | What it means | DNS behaviour |
|---|---|---|
Online | Probes are passing | Returned |
Degraded | Probes are failing | Not returned, unless every endpoint is Degraded |
CheckingEndpoint | First probe result hasn't arrived yet; temporary after adding or enabling | Returned |
Disabled | An admin disabled the endpoint | Not returned, not probed |
Inactive | The profile is disabled while the endpoint is enabled | NXDOMAIN |
Stopped | The web app isn't running, or a nested child profile is disabled or inactive | Not returned, not probed |
Warning
When every endpoint in a profile is Degraded, Traffic Manager treats them all as healthy and returns them all. Traffic keeps flowing, so a wrong probe path can go unnoticed for months. Failover then doesn't work when you need it, because no endpoint's health is really being measured.
Step 2: reproduce the probe
The probe is a GET using the protocol, port and path from the profile. An endpoint is healthy only on a 200, or on a code you listed in the expected status code ranges. A 30x counts as a failure unless you listed it, and Traffic Manager doesn't follow the redirect.
Run this from outside your network, using the same path. curl doesn't follow redirects by default, which is what you want here:
curl -sS -k -o /dev/null --max-time 10 \
-w "status=%{http_code} time=%{time_total}s redirect=%{redirect_url}\n" \
https://app-prod-eus.azurewebsites.net/health
-k ignores certificate errors, because the probe doesn't fail on them. Don't read a pass here as proof that your certificate is valid.
If the app sits behind a shared front end, also test with the host name you expect it to serve:
curl -sS -k -o /dev/null --max-time 10 -w "status=%{http_code}\n" \
-H "Host: app.contoso.com" https://20.0.0.10/health
Step 3: match the result to the cause
| Probe result | Likely cause | Fix |
|---|---|---|
| 301 or 302 | Probing HTTP against an HTTPS-only site, or the path redirects to a login page | Probe HTTPS on 443 and use a path that returns 200 directly |
| 401 or 403 | Authentication, IP restrictions or a WAF rule on the probe path | Serve the health path anonymously and allow AzureTrafficManager |
| 400, 404 or a default site page | Wrong path, or the wrong site answered because of the host header | Correct --path; add a custom Host header |
| 5xx | The health page or a dependency is failing | The endpoint is probably really unhealthy; fix the app |
| Timeout, connection refused | NSG, firewall or closed port; or the health page is too slow | Open the path for the service tag; make the page faster |
200 from curl, still Degraded | Your test differs from the probe: another port, path, source or host name | Compare with the values from step 1 |
Wrong protocol, port or path
az network traffic-manager profile update -g rg-prod -n tm-prod \
--protocol HTTPS --port 443 --path /health
A TCP monitor only checks that the connection succeeds, so it won't notice an app returning 500s. Use HTTP or HTTPS against a real health page where you can.
Probes blocked at the network or app layer
Probes come from Traffic Manager's public addresses, published as the AzureTrafficManager service tag.
# NSG in front of a VM or load balancer
az network nsg rule create -g rg-prod --nsg-name nsg-web \
--name AllowTrafficManagerProbes --priority 300 --direction Inbound \
--access Allow --protocol Tcp --source-address-prefixes AzureTrafficManager \
--destination-port-ranges 443
# App Service access restrictions
az webapp config access-restriction add -g rg-prod -n app-prod-eus \
--rule-name AllowTrafficManager --priority 200 --action Allow \
--service-tag AzureTrafficManager
To trace a blocked flow, see the NSG cheat sheet. Behind an Application Gateway WAF, check its logs for blocks on the probe path.
Host header mismatch
By default the probe's host name comes from the endpoint target. If the origin only answers for your custom domain, set the header on the endpoint, or on the profile if it applies to all of them. Asterisks aren't supported in custom Host headers.
az network traffic-manager endpoint update -g rg-prod --profile-name tm-prod \
-n eus-app --type externalEndpoints \
--custom-headers host=app.contoso.com
A redirect you can't remove
If the path has to redirect, accept the code explicitly. Keep the list narrow:
az network traffic-manager profile update -g rg-prod -n tm-prod \
--status-code-ranges 200-299 301-301
Caution
Traffic Manager doesn't probe the redirect target. If you accept a 301, the endpoint stays Online even when the app behind the redirect is down. Use this as a stopgap and move to a dedicated health path.
Stopped, Inactive and nested endpoints
Stoppedon an App Service endpoint: check that the app is running with the command below. Microsoft's docs also list a plan below the Standard tier as a cause.Inactive: the profile is disabled, or all its endpoints are disabled or stopped, and clients may get NXDOMAIN. Enable it with--status Enabled.- Nested endpoint Degraded: the parent doesn't probe the child. It uses the health of the child's endpoints and treats the child as unavailable when fewer than
MinChildEndpoints(default 1) are healthy. Check the child profile first, and lower the threshold only if the remaining capacity can carry the load. - Private or non-routable targets: probes can't reach them, so those endpoints need Always serve (
--always-serve Enabled). That turns off health checks, so Traffic Manager won't fail over from them.
az webapp show -g rg-prod -n app-prod-eus --query state -o tsv
az network traffic-manager profile update -g rg-prod -n tm-prod --status Enabled
Step 4: confirm the recovery
With the default settings (30-second interval, 3 tolerated failures, 10-second timeout), an endpoint goes Degraded on the fourth consecutive failure. It returns to Online once probes pass again. Watch the metric, where 1 means up and 0 means down:
tmId=$(az network traffic-manager profile show -g rg-prod -n tm-prod --query id -o tsv)
az monitor metrics list --resource "$tmId" \
--metric ProbeAgentCurrentEndpointStateByProfileResourceId \
--filter "EndpointName eq '*'" --aggregation Maximum --interval PT1M --offset 1h \
-o table
Then check what DNS is handing out. Clients keep any cached answer until the TTL expires, and some resolvers hold it longer:
dig +short tm-prod.trafficmanager.net
Prevent it next time
- Use a dedicated health path. It should be anonymous, fast, free of redirects, and check the dependencies that really stop the app from serving.
- Alert per endpoint. A profile with every endpoint Degraded still serves traffic, so you need the alert to learn about it:
az monitor metrics alert create -g rg-prod -n tm-endpoint-down --scopes "$tmId" \
--condition "max ProbeAgentCurrentEndpointStateByProfileResourceId < 1 where EndpointName includes *" \
--window-size 5m --evaluation-frequency 1m --action ag-oncall
- Tune failover deliberately.
--interval 10 --timeout 5detects failures faster but sends more probes; check current pricing. Don't raise--max-failuresto quiet a flapping endpoint, because that also slows real failover. - Test it in DR drills. Fail one endpoint on purpose and confirm that DNS changes.