< HTTP/1.1 503 Service UnavailableHTTP 503 Service Unavailable: Causes, Fixes and Retry-After
Fix HTTP 503 Service Unavailable: nginx no live upstreams, Kubernetes endpoints, ALB healthy hosts, Cloudflare, and a maintenance page with Retry-After.
- Cacheable
- Only with explicit freshness
- Retry?
- Yes: honor Retry-After
- Usually sent by
- Origin, load balancer or CDN
- Spec
- RFC 9110 §15.6.4
On this page
TL;DR: HTTP 503 Service Unavailable means the server cannot handle your request right now, often because of overload or maintenance. Find who answered, check backend health, and if you serve 503 on purpose, send
Retry-After.
What it means
RFC 9110 section 15.6.4: the server is currently unable to handle the request due to temporary overload or scheduled maintenance. The key difference from 500 is that 503 says retrying later is expected to work. Some proxies also use it when no backend is available. An ALB with all targets unhealthy can fail open and route to them.
Server responses and client messages below are illustrative.
HTTP/1.1 503 Service Unavailable
Retry-After: 120
Content-Type: text/html
What you see in your client
These messages assume the response reason is Service Unavailable; a server can use different wording.
- Axios:
AxiosError: Request failed with status code 503. With the defaultvalidateStatus, Axios rejects this response. Inspecterror.response.status,error.response.headersanderror.response.databefore changing the request. Axios source. - fetch: the promise resolves with
response.status === 503andresponse.ok === false. Checkokyourself; acatchblock alone misses HTTP error responses. Fetch behaviour. - Python requests:
requests.exceptions.HTTPError: 503 Server Error: Service Unavailable for url: https://example.com/api. This appears when you callresponse.raise_for_status(). Save the response body and headers before raising. Requests source. - curl -f:
curl: (22) The requested URL returned error: 503. Usecurl -sS -D - https://example.com/apiwhile diagnosing so you retain the error body. curl source. - .NET:
System.Net.Http.HttpRequestException: Response status code does not indicate success: 503 (Service Unavailable).This is the English message fromEnsureSuccessStatusCode()with that reason phrase. InspectStatusCodeand read the body before calling it. Runtime source, message resource. - Spring WebClient:
org.springframework.web.reactive.function.client.WebClientResponseException$ServiceUnavailable: 503 Service Unavailable from GET https://example.com/api.retrieve()uses this subclass by default; readgetResponseBodyAsString(). RestTemplate usesHttpServerErrorException.ServiceUnavailablefor this 5xx status. WebClient source, RestTemplate handler.
Who sent it?
| Signal | Layer and typical cause |
|---|---|
Server: nginx, page “503 Service Temporarily Unavailable” | nginx: limit_req/limit_conn rejection, or a deliberate return 503 |
Server: awselb/2.0 and an empty or minimal body | ALB: no registered targets, or all registered targets are unused |
ingress-nginx page, log Service "ns/name" does not have any active Endpoint | Kubernetes: no Ready pods behind the Service |
Server: cloudflare with an origin error page, or Cloudflare branded page | Origin sent 503, or Cloudflare generated a data-center connectivity error |
X-Cache: Error from cloudfront, “The request could not be satisfied” | Origin overload, CloudFront capacity or an edge-function limit/error |
| App log shows pool exhausted, queue full | The application itself is shedding load |
nginx request-limit log (sample request identifiers):
[error] 29#29: *513 limiting requests, excess: 10.300 by zone "api", client: 203.0.113.9
Common causes by stack
- nginx:
limit_req_statusandlimit_conn_statusboth default to 503. A burst beyondlimit_req’s allowance is a rejection; inspectlimiting requestsin the error log. For deliberate per-client throttling, configure 429. Request limits, connection limits. - Laravel:
php artisan down --retry=60activates maintenance and suppliesRetry-After. Runphp artisan upwhen deployment is complete; check that every serving instance has left maintenance. Laravel maintenance mode. - ASP.NET Core: rate-limiting middleware’s
RejectionStatusCodedefaults to 503. If the rejection body says too many concurrent requests, inspect the limiter policy and queue before adding retries; set 429 for a per-client quota. RateLimiterOptions. - Kubernetes ingress-nginx: an empty backend produces 503; the controller warns
Service "namespace/name" does not have any active Endpoint.Check selectors and readiness, then inspect EndpointSlices. An existing endpoint with a refused port points toward 502 instead. Controller source, Service debugging. - AWS ALB: no registered targets or all targets
unusedproduces ALB-generated 503. All targets merely unhealthy is different: ALB can fail open. ALB errors, health-check behaviour. - Cloudflare/CloudFront: identify the error body before changing origin capacity. Cloudflare documents its own 503 connectivity errors; CloudFront lists origin overload, edge capacity and function execution limits. Cloudflare 503, CloudFront 503.
Fix it
- Confirm the backends are up and passing health checks:
curl -sv http://BACKEND:PORT/healthfrom the proxy host. - nginx
limit_req/limit_conn: both reject with 503 by default. Setlimit_req_status 429;andlimit_conn_status 429;so clients see the right code (see 429). - Kubernetes: inspect
kubectl get endpointslices -l kubernetes.io/service-name=my-svc -o yaml, thenkubectl describe pod POD. Match Ready endpoints to the Service selector and port. Check rollout capacity and the termination grace period when failures coincide with deploys. - ALB: in the target group, check “healthy host count” and the health check path, port and success codes; a 503 from the ALB itself with no targets registered is a configuration issue, not load.
- Overload: look at CPU, memory, database connection pool size and worker counts (gunicorn
--workers, PHP-FPMpm.max_children); autoscale or shed load deliberately. - Cloudflare in front: if the 503 body is your origin’s, fix the origin; if it contains
cloudflareorcloudflare-nginx, use Cloudflare’s 503 troubleshooting path. For distinct origin connection failures, inspect the 52x codes (see 520 to 524).
Return 503 on purpose: maintenance mode
Send Retry-After, mark the response uncacheable, and keep your health endpoint working. Create /var/www/maintenance/index.html before enabling this nginx configuration; its named error location preserves the 503 status. Retry-After is either seconds or an HTTP date:
server {
error_page 503 @maintenance;
location / {
return 503;
}
location = /health {
return 200 "ok\n";
}
location @maintenance {
add_header Retry-After 3600 always;
add_header Cache-Control "no-store" always;
root /var/www/maintenance;
rewrite ^ /index.html break;
}
}
// Express: shed load or enter maintenance with a correct Retry-After
app.use((req, res, next) => {
if (!maintenanceMode) return next()
res.set({ 'Retry-After': '3600', 'Cache-Control': 'no-store' })
res.status(503).type('text/html').send('<h1>Back soon</h1>')
})
Clients must accept both forms of the header:
function retryAfterMs(res) {
const v = res.headers.get('retry-after')
if (!v) return null
const n = Number(v)
if (Number.isFinite(n)) return n * 1000
const t = Date.parse(v)
return Number.isNaN(t) ? null : Math.max(0, t - Date.now())
}
SEO during maintenance
Serve temporary maintenance at the original URLs with 503 and Retry-After. Google reduces crawling for 5xx responses, ignores their response content and initially preserves indexed URLs; persistent errors eventually remove them. Returning 200 makes the maintenance body eligible for indexing. Restore normal responses promptly rather than redirecting every URL to one maintenance page. Google’s HTTP guidance.
Retrying a concurrency rejection
For a 503 body containing "too many concurrent requests", reduce parallel requests and queue work on the client. Wait for Retry-After when supplied; otherwise use bounded exponential backoff with jitter. Each retry consumes capacity too. For a POST that changes state, check its result or use the service’s idempotency mechanism before repeating it.
Reproduce
curl -sI https://example.com/ | grep -iE '^(HTTP|retry-after|server|via|x-cache|cf-ray)'
Related codes
- 500 Internal Server Error: the app failed unexpectedly, not unavailable.
- 502 Bad Gateway: the proxy got an invalid or refused response from the upstream.
- 504 Gateway Timeout: a gateway timed out connecting or waiting for an upstream.
- 429 Too Many Requests: you specifically are being throttled.
See also the comparison 502 vs 503 vs 504: how to tell the three gateway errors apart from the nginx error log and Cloudflare codes.
Frequently asked questions
What does 503 Service Unavailable mean?
The server cannot handle the request right now because it is overloaded, in maintenance, or has no healthy backend to pass it to. RFC 9110 treats it as temporary, and a server may send Retry-After to say when to come back.
How do I fix nginx 503 Service Temporarily Unavailable?
Check the nginx error log. A limit_req or limit_conn rejection returns 503 by default, a maintenance flag or return 503 rule may be active, and deliberate maintenance middleware may return it. A stopped upstream commonly causes 502 instead. ("no live upstreams" is a 502 in nginx, not a 503; see the 502 page.)
How long does a 503 error last?
It lasts as long as the cause: seconds for a rolling deploy or autoscale, minutes to hours for maintenance, until the incident is fixed for outages. If Retry-After is present, it is the server estimate; if not, use exponential backoff.
Does a 503 hurt SEO?
A short 503 with Retry-After is the correct signal for maintenance and Google slows crawling and keeps the pages indexed. Persistent 5xx responses can eventually remove URLs from the index, so restore normal responses promptly.
What is the difference between 500 and 503?
500 means the application hit an unexpected error handling the request. 503 means it could not take the request at all (overload, maintenance, no healthy backend) and retrying later is expected to work.
What does a 503 saying too many concurrent requests mean?
The service is rejecting simultaneous work. Reduce client parallelism, queue requests and respect Retry-After when supplied; otherwise use bounded backoff with jitter. Check the result of a state-changing request before repeating it.
Why is my Kubernetes ingress returning 503?
Usually the Service has no ready endpoints: pods are failing readiness probes or the selector does not match any pod. A wrong targetPort can instead leave endpoints present but cause a connection failure and 502. ingress-nginx logs Service "namespace/name" does not have any active Endpoint.
Sources
- MDN Web Docs: 503 Service Unavailabledeveloper.mozilla.org
- RFC 9110 Section 15.6.4: 503 Service Unavailablerfc-editor.org
- RFC 9110 Section 10.2.3: Retry-Afterrfc-editor.org
- Google Search Central: HTTP status codes and network errorsdevelopers.google.com
- AWS: Troubleshoot Application Load Balancer HTTP 503docs.aws.amazon.com
Related
502 vs 503 vs 504: Which Gateway Error Is It
502, 503 and 504 side by side: who generated each one, what the upstream did, the nginx error-log line that proves it, and the Cloudflare 52x and ALB equivalents.
509 Bandwidth Limit Exceeded (cPanel Hosting)
cPanel documents 509 Bandwidth Limit Exceeded for an administrator-imposed transfer limit. Confirm account usage in WHM and adjust the quota or wait.
529 Site Is Overloaded (SSL Labs and Anthropic)
529 is a non-standard overload code used by SSL Labs and Anthropic. Identify the API, inspect overloaded_error, and back off without confusing it with 429.
501 Not Implemented
The server doesn't support the functionality required to fulfill the request. Learn about unimplemented features.
HTTP 502 Bad Gateway: nginx, ALB and Cloudflare Fixes
Fix 502 Bad Gateway: decode nginx error-log lines, php-fpm sockets, ALB keep-alive mismatches and Cloudflare 502 vs 52x, with curl checks.