Guides

7 min readUpdated

503 Service Unavailable: what it means and how to fix it

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.

CodeMeaningTypical sourceUsually deliberate?
500 Internal Server ErrorThe application failed while handling the requestApplication codeNo
502 Bad GatewayA proxy got an invalid response, or none, from upstreamCDN, load balancer, reverse proxyNo
503 Service UnavailableThe server is refusing work for nowMaintenance mode, queue limits, rate limiter, empty backend poolOften
504 Gateway TimeoutA proxy gave up waiting for upstreamCDN, load balancer, reverse proxyNo
429 Too Many RequestsThis client sent too many requestsRate limiterYes

02What causes a 503 error?

Six causes account for most 503s, and each leaves a different trace.

CauseWho sends the 503How to recognise it
Maintenance modeThe application or web serverA maintenance page; WordPress shows "Briefly unavailable for scheduled maintenance. Check back in a minute."
Queue or connection limit reachedLoad balancer or proxyHAProxy returns 503 when no server is available, or when a request waits in the queue longer than timeout queue
Application pool stoppedThe web server in front of the applicationIIS disables a pool after 5 worker crashes within 5 minutes by default (rapid-fail protection) and answers 503
No healthy backendsLoad balancerHAProxy answers 503 when every server is down; AWS documents an ALB 503 when a target group has no registered targets
Rate limitingProxy or WAFnginx's limit_req and limit_conn reject with 503 unless told otherwise; the error log shows "limiting requests" or "limiting connections"
Overload or attackOrigin, proxy or protection layer503s 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.

First checks for a 503 (replace example.com and paths)
# 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 3

04When 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.

A per-client login limit that answers 429 (tested with nginx 1.22.1)
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.

Switchable maintenance page with 503 and Retry-After (tested with nginx 1.22.1)
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

No. By definition a 503 describes a temporary condition: maintenance, overload or a deliberate refusal of work. Most clear within minutes. A 503 that lasts for hours means something is stuck, such as a maintenance flag left in place, an application pool that keeps crashing, or a load balancer with no healthy servers, and the site operator needs to act on it.

A short one does not. Google's crawlers slow down when they see 5xx responses and keep already indexed URLs for a while, recovering the crawl rate once pages return 2xx again. Google recommends a 503 for interruptions of one or two days at most, and warns never to serve 503 for robots.txt, which stops crawling of the whole site.

Use 429 Too Many Requests for limits applied to one client, because it says the client is the problem, and keep 503 for situations where the whole service cannot cope. nginx returns 503 by default for limit_req and limit_conn, so change it with limit_req_status and limit_conn_status. Both codes may carry a Retry-After header to tell the client when to try again.

It tells the client how long to wait before trying again, either as a number of seconds such as Retry-After: 600 or as an HTTP date. RFC 9110 allows it on 503 responses to state how long the service is expected to be unavailable. Well-behaved clients and crawlers use it to reschedule their next request rather than hammering a server that has already said it is busy.

WordPress creates a .maintenance file while it installs updates and answers every request with a 503 and Retry-After: 600 while the file applies. If an update fails, the file can be left behind; deleting it ends the message at once. WordPress stops honouring the file ten minutes after the update started, so a message that lasts much longer usually comes from a maintenance plugin.

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.

  1. 01RFC 9110, HTTP Semantics, section 15.6.4: 503 Service Unavailable · RFC Editor
  2. 02RFC 9110, section 10.2.3: Retry-After · RFC Editor
  3. 03RFC 6585: Additional HTTP Status Codes (429 Too Many Requests) · RFC Editor
  4. 04Module ngx_http_limit_req_module (limit_req_status) · nginx.org
  5. 05Module ngx_http_headers_module (add_header ... always) · nginx.org
  6. 06HAProxy 2.8 configuration manual: status codes and timeout queue · HAProxy
  7. 07Failure settings for an application pool (rapid-fail protection) · Microsoft Learn
  8. 08WordPress source: wp_maintenance() in wp-includes/load.php · WordPress on GitHub
  9. 09Pausing or disabling a website · Google Search Central
  10. 10How HTTP status codes affect Google's crawlers · Google Search Central
  11. 11Troubleshoot your Application Load Balancers: HTTP 503 · AWS

Last updated