What it means: Envoy found no usable endpoint in the target cluster and answered 503 itself, without contacting your backend.
Free online tool — HTTP status code checker: instant results, no signup.
Requests the address and shows the status code and what the server sends back.
no healthy upstream is a local reply generated by Envoy's router filter, not by your application. When the load balancer cannot pick a host for the request, the router calls sendNoHealthyUpstreamResponse(), which sends the literal body no healthy upstream with status 503 by default and marks the request with the response flag UH (NoHealthyUpstream) in the access log (router.cc, access log response flags). You see it behind anything built on Envoy: Istio sidecars and gateways, Envoy Gateway, Contour, Emissary, Gloo and many API gateways.
The key point is that no connection to a backend was attempted. Envoy decided before connecting that the cluster had nothing to send to. That separates it from upstream connect error or disconnect/reset before headers (flag UF: a host was chosen, but the TCP or TLS connection to it failed) and from NR (no route matched, usually a 404). It is also not a timeout: that is upstream request timeout with a 504.
"Healthy" is narrower than it sounds. With default settings Envoy does not stop routing when every host fails its health checks: below the panic threshold (50% available hosts by default) it ignores health status and balances across all hosts (panic threshold). So on a default cluster the message usually means the cluster has zero hosts, not that its hosts are sick.
| Cause | How to recognise it | Fix |
|---|---|---|
| No Ready pods behind the Service | kubectl get endpointslices lists no ready addresses; pods are CrashLoopBackOff or fail their readiness probe | Fix the crash or the readiness probe; scale the Deployment above zero |
| Service selector matches no pods | Endpoints empty although pods run; labels differ by a typo or after a rename | Make spec.selector equal to the pod template labels |
| Istio subset matches no pods | A DestinationRule subset (for example version: v2) has labels no pod carries; only requests routed to that subset fail | Correct the subset labels or the VirtualService route |
| Panic mode disabled | healthy_panic_threshold set to 0% or fail_traffic_on_panic enabled, and every host fails health checks | Fix the health check or the backend; re-enable panic routing if all-or-nothing is not what you want |
| DNS-based cluster resolves to nothing | A STRICT_DNS or LOGICAL_DNS cluster whose hostname does not resolve from the proxy | Fix the hostname or the resolver the proxy uses |
| Rollout or scale to zero | Errors only during deploys or at night when autoscaling removes the last replica | Keep a minimum replica count; use a PodDisruptionBudget and readiness gates |
Outlier detection rarely empties a whole cluster by itself: by default it ejects at most 10% of hosts (max_ejection_percent), and an ejection lasts base_ejection_time, 30 seconds by default, multiplied by the number of previous ejections (outlier_detection.proto). If you raised that percentage to 100, a burst of 5xx can eject every host and produce this error until the ejections expire.
In a mesh, the cluster's endpoints come from the control plane, which builds them from Kubernetes EndpointSlices. A pod that is not Ready is not an endpoint, so a failing readiness probe removes the pod from Envoy's view before any Envoy health check runs. Start from Kubernetes, then look at what the proxy actually received:
kubectl get pods -l app=reviews -o wide
kubectl get endpointslices -l kubernetes.io/service-name=reviews -o wide
istioctl proxy-config endpoint deploy/productpage --cluster "outbound|9080|v2|reviews.default.svc.cluster.local"An empty endpoint list for the exact cluster name in the error path means the subset or Service has no members. If the list has entries marked UNHEALTHY, look at health checks and outlier detection. In a sidecar you can also read Envoy's own view:
kubectl exec deploy/productpage -c istio-proxy -- pilot-agent request GET clusters | grep reviewsEach host line ends with its health status: healthy, or one or more of /failed_active_hc, /failed_eds_health and /failed_outlier_check (Envoy admin /clusters). No lines at all for the cluster is the zero-hosts case.
curl -sv https://example.com/api/ 2>&1 | grep -iE '^< (HTTP|server)|no healthy'. A server: envoy header (unless your config rewrites it) and the exact body mean the proxy answered.UH carries the upstream cluster name if your format includes %UPSTREAM_CLUSTER%.kubectl describe pod POD | grep -A5 Readiness. A probe pointing at the wrong port or path keeps every pod out of rotation.kubectl exec deploy/productpage -c istio-proxy -- curl -s -o /dev/null -w '%{http_code}\n' http://reviews:9080/health.pilot-agent request GET stats | grep -E 'reviews.*outlier_detection.ejections_active'.istioctl proxy-config endpoint deploy/productpage | grep reviews
# 10.0.1.17:9080 HEALTHY OK outbound|9080|v2|reviews.default.svc.cluster.local
curl -s -o /dev/null -w '%{http_code}\n' https://example.com/api/
# 200The endpoint list must show at least one HEALTHY host for the cluster the route uses, and repeated requests must stop returning the 19-byte no healthy upstream body. To check the public URL from outside your network, run it through the HTTP status checker: it shows the status code and what the server sends back.
HTTP status codes are three-digit numbers that the server returns for every request. They tell the browser and search crawlers what happened: success, redirect, or error.
Enter a code or keyword — instant result with description.
1xx Informational, 2xx Success, 3xx Redirect, 4xx Client Error, 5xx Server Error.
For each code: typical causes and concrete steps to resolve.
How each code affects indexing and link juice transfer in Google.
choosing correct status
redirect optimization
5xx error diagnostics
landing page check
HTTP monitor alerts when response code changes or 5xx appears.
Sign up freeNo. Envoy generates the body itself when it has no host to send the request to, so your application never sees the request. The cause is in endpoint discovery, labels, readiness or health checking.
Running is not Ready. A pod that fails its readiness probe, or whose labels do not match the Service selector or the Istio subset, is not an endpoint, so Envoy sees an empty cluster.
Not with default settings. When fewer than 50% of hosts are available Envoy enters panic mode and sends traffic to all hosts anyway, unless the panic threshold is 0% or fail_traffic_on_panic is enabled.
Old pods leave the endpoint list before new ones become Ready, or a scale-to-zero removes the last replica. Keep a minimum replica count and make the rollout wait for readiness.
UH stands for NoHealthyUpstream: there were no healthy upstream hosts in the cluster, and Envoy returned 503 itself.
Free plan — 10 monitors, checks every 5 min, no card required. Upgrade for 1-minute interval and multi-region monitoring.