503 Service Unavailable means the server can't handle the request right now because it's overloaded, down for maintenance, or has no healthy backend to route to. Unlike a 500, it signals a temporary condition. The server may include a Retry-After header with its estimate of when to try again.
HTTP/1.1 503 Service Unavailable
Retry-After: 120
Content-Type: application/json
{"error": "maintenance", "message": "Scheduled maintenance, back in about 2 minutes"}
Common causes
- Planned maintenance. The app or a load balancer returns a maintenance page with 503.
- Overload. Every worker or thread is busy, connection pools are exhausted, or an autoscaler hasn't caught up yet.
- No healthy backends. An AWS ALB target group with no healthy targets, a Kubernetes Service with no ready pods, or a deploy that took every instance down at once.
- Rate or connection limits. nginx
limit_reqandlimit_connreject excess requests with503by default. - A dependency outage. The app deliberately answers 503 when its database or a required service is down.
How to fix it as a visitor
Wait and retry. Most 503s clear within minutes. Check the service's status page or social accounts for maintenance notices. Repeated refreshing during an overload makes it worse for everyone.
How to fix it as a developer or ops
| Where | Likely cause | Fix |
|---|---|---|
| nginx | limiting requests in the error log | Raise the limit_req rate or burst, or set limit_req_status 429; so clients see the correct code. |
| ingress-nginx / Kubernetes | Service has no ready endpoints | kubectl get endpoints <svc>; fix failing readiness probes or the selector. |
| AWS ALB | No registered or healthy targets | Check target health and the health-check path; make sure deploys keep some targets in service. |
| Cloudflare | Origin or Worker overloaded | Check origin capacity; a 503 from Cloudflare itself can also mean a Worker exceeded its limits. |
| App servers | Workers saturated | Add workers or instances, fix slow queries, add caching. |
Two general practices prevent most 503s during deploys: rolling deploys that keep some instances serving, and readiness checks that only route traffic to instances that have finished starting.
Handling a 503 as an API client
- Honour
Retry-After. It's either a number of seconds (Retry-After: 120) or an HTTP date. Don't retry before then. - Otherwise back off exponentially with jitter: 1s, 2s, 4s, 8s plus a random offset, with a cap on attempts. See exponential backoff.
- Only retry safe requests automatically.
GET,PUTandDELETEare idempotent. Retry aPOSTonly if the API supports idempotency keys, or you may create the same thing twice. - Use a circuit breaker for dependencies that return 503 repeatedly, so your own service fails fast instead of piling up waiting requests.
503s on a webhook endpoint
Deploys are the most common source of webhook 503s. For a minute or two, every delivery fails. Providers retry, but their retry windows are short and each one is different, and a longer outage can run past the window entirely.
Webhook Relay durable retries take that timing out of the provider's hands. Every event is stored before delivery and retried with exponential backoff for up to 30 days, so webhooks sent during a deploy or outage wait until your endpoint returns 2xx. If the 503s come from overload rather than downtime, outbound throttling releases events at a fixed rate or concurrency so a burst never saturates your workers in the first place.
When to return 503 from your own API
Return 503 when the whole service, or a whole endpoint, is temporarily unable to serve anyone: maintenance, overload, a required dependency down. Include Retry-After (seconds or an HTTP date) so well-behaved clients wait instead of hammering you. If only one client is over its quota, use 429 Too Many Requests. If your code simply crashed, that's a 500.
