HTTP 503 Service Unavailable

Service Unavailable

HTTP 503 is Service Unavailable: the server can’t handle the request right now because of a temporary overload or scheduled maintenance, and expects to recover after some delay (RFC 9110 section 15.6.4). It’s the 5xx that promises a “later”. The server may send Retry-After to say when; RFC 9110 also notes that an overloaded server doesn’t have to use 503 and may simply refuse connections.

The exchange

HTTP/1.1 503 Service Unavailable
Retry-After: 3600
Content-Type: text/html

<h1>Down for maintenance until 10:00 UTC</h1>

Retry-After takes seconds, or an HTTP date like Fri, 16 Oct 2026 10:00:00 GMT (RFC 9110 section 10.2.3). A client should wait at least that long, and back off exponentially when there’s no header. Idempotent requests are safe to retry.

Maintenance and search engines

For planned downtime, a 503 tells Googlebot to slow down, and indexed pages keep their place while the outage is short. If the errors go on, Google drops the URLs. A maintenance page served with 200 is worse: to Google it’s the new content of every URL that shows it. robots.txt matters too: Google reads a 5xx on /robots.txt as “the whole site is disallowed” until it gets a real answer, so crawling stops.

Who sends it

  • nginx limit_req and limit_conn reject with 503 by default. For rate limits, set limit_req_status 429;: that’s what they are.
  • AWS ALB answers 503 when the target group has no registered targets. With targets registered but all unhealthy, it fails open and sends traffic to all of them.
  • Kubernetes ingress-nginx answers 503 when the Service has no ready endpoints: every pod failing its readiness probe, or a selector that matches nothing.
  • Go’s http.TimeoutHandler answers 503 when a handler runs past its limit.
  • Your own app, when shedding load, or from a readiness endpoint during graceful shutdown, so the load balancer stops sending it traffic.

503 or another code

A dependency outage your app can’t work around is a fair 503. A bug is a 500, a rate limit is a 429, and a proxy whose upstream didn’t answer in time sends 504. 503 isn’t heuristically cacheable, though CloudFront caches it for 10 seconds by default.

In your stack

A maintenance switch in nginx:

server {
    listen 80;
    server_name example.com;
    add_header Retry-After 3600 always;    # without "always", add_header skips error responses
    error_page 503 /maintenance.html;

    location = /maintenance.html {
        root /srv/www;
        internal;
    }

    location / {
        return 503;
    }
}
res.set('Retry-After', '120').status(503).json({ error: 'maintenance' });    // Express
return "Down for maintenance", 503, {"Retry-After": "3600"}                 # Flask
w.Header().Set("Retry-After", "120")
http.Error(w, "down for maintenance", http.StatusServiceUnavailable)

Try it in the HTTP Status Codes

503 Service Unavailable

  1. 503Service Unavailable5xx Server error

    The server cannot handle the request right now, usually because it is overloaded or down for maintenance. Retry-After says when to come back.

    When to use it

    Planned maintenance, load shedding, and upstreams that are briefly out of service.

    Common mistakes

    Leaving it up for hours with no Retry-After, and returning it for a permanent failure that is really a 500.

    Not cacheable by defaultRFC 9110

Every code, by class

Codes marked unofficial come from one server’s own convention — nginx, Cloudflare, cPanel, Microsoft — and are not in the IANA registry.

Runs in your browser. Nothing you enter is sent anywhere.