Quick answer
A 503 Service Unavailable error means the server is reachable but temporarily unable to handle the request, usually because of planned maintenance, overload, an exhausted worker or queue limit, a stopped application pool, or a rate limiter that answers with 503. Unlike a 502 or 504, the server sending a 503 is often working exactly as configured. It should be short-lived, and a Retry-After header tells browsers and crawlers when to come back.
01What does 503 Service Unavailable mean?
RFC 9110 defines 503 as a server being "currently unable to handle the request due to a temporary overload or scheduled maintenance, which will likely be alleviated after some delay". Two parts of that definition matter: the condition is temporary, and it is the server's own state. Unlike a 502 or a 504, a 503 is often the deliberate answer of a component working as configured: a maintenance switch, a full queue, a rate limiter or a load balancer with no healthy backends.
The specification adds that a server may send a Retry-After header to say when to try again, and that it is not obliged to use 503 at all when overloaded: some servers simply refuse the connection. The table sets 503 beside its closest neighbours; the full list is in HTTP status codes.
| Code | Meaning | Typical source | Usually deliberate? |
|---|---|---|---|
| 500 Internal Server Error | The application failed while handling the request | Application code | No |
| 502 Bad Gateway | A proxy got an invalid response, or none, from upstream | CDN, load balancer, reverse proxy | No |
| 503 Service Unavailable | The server is refusing work for now | Maintenance mode, queue limits, rate limiter, empty backend pool | Often |
| 504 Gateway Timeout | A proxy gave up waiting for upstream | CDN, load balancer, reverse proxy | No |
| 429 Too Many Requests | This client sent too many requests | Rate limiter | Yes |
02What causes a 503 error?
Six causes account for most 503s, and each leaves a different trace.
| Cause | Who sends the 503 | How to recognise it |
|---|---|---|
| Maintenance mode | The application or web server | A maintenance page; WordPress shows "Briefly unavailable for scheduled maintenance. Check back in a minute." |
| Queue or connection limit reached | Load balancer or proxy | HAProxy returns 503 when no server is available, or when a request waits in the queue longer than timeout queue |
| Application pool stopped | The web server in front of the application | IIS disables a pool after 5 worker crashes within 5 minutes by default (rapid-fail protection) and answers 503 |
| No healthy backends | Load balancer | HAProxy answers 503 when every server is down; AWS documents an ALB 503 when a target group has no registered targets |
| Rate limiting | Proxy or WAF | nginx's limit_req and limit_conn reject with 503 unless told otherwise; the error log shows "limiting requests" or "limiting connections" |
| Overload or attack | Origin, proxy or protection layer | 503s across all URLs at once, with high request rates or exhausted workers |
03How to tell which 503 you are seeing
Start with the response itself, then the logs of the component that sent it. A deliberate 503 usually carries clues: a Retry-After header, a branded page (whose branding shows whether the edge or the origin answered) or a matching log line.
In nginx, a rate-limit rejection appears in the error log as limiting requests, excess: 0.989 by zone "perip", followed by the client address and request. If those lines line up with the 503s, the server is healthy and your own limit is turning visitors away. Note that nginx's built-in page reads "503 Service Temporarily Unavailable"; the status code is the same.
# status line, Retry-After and the server that answered
curl -sI https://example.com/ | grep -iE '^(HTTP|retry-after|server)'
# WordPress maintenance flag left behind by an update
ls -la /var/www/html/.maintenance
# nginx rate limiting: count rejections and see which zone fired
grep -c 'limiting requests' /var/log/nginx/error.log
grep 'limiting requests' /var/log/nginx/error.log | tail -n 304When rate limiting returns 503
This is the 503 most often mistaken for an outage. nginx's limit_req and limit_conn modules use 503 as their default rejection status, so an over-tight limit makes a healthy site look down to exactly the users who use it most. The dedicated status for a client sending too much is 429 Too Many Requests, defined in RFC 6585, which may also carry Retry-After.
Return 429 for per-client limits and keep 503 for genuine capacity problems, so monitoring and people can tell the two apart at a glance. Google's crawlers treat both 429 and 5xx as a signal to slow down, so neither is a way to block crawling. Choosing sensible limits is covered in what rate limiting is.
limit_req_zone $binary_remote_addr zone=perip:10m rate=10r/s;
server {
location /login {
limit_req zone=perip burst=20 nodelay;
limit_req_status 429;
# proxy_pass or fastcgi_pass as usual
}
}05How to serve a maintenance 503 with Retry-After
For planned work, 503 is the right status: it tells browsers, monitors and crawlers that the outage is temporary. Add Retry-After with either a number of seconds or an HTTP date, as RFC 9110 specifies, for example Retry-After: 600 or Retry-After: Fri, 31 Dec 1999 23:59:59 GMT.
Google's guidance is to use a 503 only for short interruptions of one or two days at most, and never to return 503 for robots.txt, because that blocks crawling of the whole site. The configuration below keeps robots.txt out of maintenance for that reason. Create the flag file to switch maintenance on and delete it to switch off, without a reload. The always parameter matters: without it nginx adds headers only to successful responses, so Retry-After would be missing from the 503.
location / {
if (-f /var/www/maintenance.on) {
return 503;
}
try_files $uri $uri/ =404;
}
location = /robots.txt {
try_files $uri =404; # never serve robots.txt as 503
}
error_page 503 /maintenance.html;
location = /maintenance.html {
root /var/www/static;
add_header Retry-After 600 always;
internal;
}06Can a DDoS attack cause 503 errors?
Yes, in three ways. An HTTP flood can exhaust an application's workers or a load balancer's queue, which then answers 503 because it has nowhere to send the request. Rate limiters tuned for normal traffic start rejecting real visitors along with the flood. And a protection layer in front of the site may itself answer some requests with 503 and Retry-After under extreme load, deliberately shedding work so the origin survives instead of collapsing.
The difference between a deliberate shed and a collapse matters. Shedding is selective and short; a collapse takes every page down and stays down. Filtering and capacity in front of the origin, as described in how to stop a DDoS attack, turn most floods into the first case. The guide to types of DDoS attacks explains which resource each kind of flood exhausts, and the signs of a DDoS attack separate a flood from an ordinary traffic spike.
07How to fix a 503 as the site owner
Fix the component that sent the 503, not the one that displayed it.
- Maintenance left on: remove the flag file or the plugin setting, after finishing or rolling back the update
- Rate limits too tight: count "limiting requests" lines, raise the rate or burst, and switch the rejection code to 429
- Empty backend pool: fix the failing health check, or the service behind it, before adding servers
- Stopped IIS application pool: find the crash in the Windows event log and fix it before starting the pool again, or rapid-fail protection will stop it again
- Overloaded queue: make requests faster or add capacity; raising queue limits only moves the wait
- Traffic surge or attack: cache what can be cached, filter abusive traffic upstream, and keep the origin address private
08What to do if you see a 503 as a visitor
A 503 is the site telling you to come back later. Wait for the time given on the page, or a few minutes, then reload; rapid repeated reloading can keep you inside a rate limit. If the site is down for everyone, nothing on your side will help. If only you get 503s while others do not, a VPN, a shared corporate address or a browser extension may be tripping a per-client limit.
Questions
Frequently asked questions
Sources
Sources and verification
Maintenance and rate-limit snippets run on nginx 1.22.1, with the 503 plus Retry-After and the 429 responses confirmed using curl 7.88.1; the limiting requests log line was reproduced; Debian 12, October 2026.
- 01RFC 9110, HTTP Semantics, section 15.6.4: 503 Service Unavailable · RFC Editor
- 02RFC 9110, section 10.2.3: Retry-After · RFC Editor
- 03RFC 6585: Additional HTTP Status Codes (429 Too Many Requests) · RFC Editor
- 04Module ngx_http_limit_req_module (limit_req_status) · nginx.org
- 05Module ngx_http_headers_module (add_header ... always) · nginx.org
- 06HAProxy 2.8 configuration manual: status codes and timeout queue · HAProxy
- 07Failure settings for an application pool (rapid-fail protection) · Microsoft Learn
- 08WordPress source: wp_maintenance() in wp-includes/load.php · WordPress on GitHub
- 09Pausing or disabling a website · Google Search Central
- 10How HTTP status codes affect Google's crawlers · Google Search Central
- 11Troubleshoot your Application Load Balancers: HTTP 503 · AWS
Last updated