Serving 503 During Planned Downtime

A 503 tells a client the server is temporarily unable to handle the request and to try again later. It is the correct response during planned maintenance, and it is the one code that lets you take a site offline without the crawler concluding that your pages are gone.

The tolerance is time-limited. A 503 that persists stops reading as maintenance and starts reading as a site that no longer works.

What the code carries

The specification defines 503 as a temporary condition, and pairs it with an optional Retry-After header that states how long the client should wait — either as a delay in seconds or as an HTTP date.

Search-engine handling follows that intent. Documented behaviour is that a 503 causes the crawler to back off and retry rather than treat the URL as removed, and that existing index entries are retained through short periods of unavailability. The crawler also reduces its request rate while it is receiving them, which is a helpful side effect if the reason you are down is load.

What is not documented is the exact point at which patience runs out. The published guidance is that maintenance responses should be short — days rather than weeks — and that prolonged 5xx responses eventually lead to URLs being dropped. Treat any specific threshold you read as inference.

The codes people use instead, and what each costs

200 with a maintenance page. The most common mistake, because it is what happens by default when someone swaps the document root. Every URL on the site now returns a valid page whose content is “we’ll be back soon.” From the crawler’s perspective the entire site has been replaced with one thin page, and re-indexing it that way is a real cost. This is the whole-site version of a soft 404.

302 to a maintenance URL. Slightly better than 200, and still wrong. You have told the crawler that every URL temporarily lives somewhere else, and the destination is a page with no content. It also creates a second problem: the maintenance URL itself returns 200 and can be indexed.

404 or 410. Worst case. You have asserted that your pages do not exist, and you have done it across every URL simultaneously.

500. Reads as a broken application rather than planned maintenance, and carries no Retry-After. It will be backed off from, but it also looks like something is wrong in a way that gets sampled and reported.

Connection refused / timeout. No response at all. Crawlers back off from these too, but you lose the ability to say anything about duration, and the failure is indistinguishable from a DNS or routing problem.

Doing it properly

The rule is: one status code, every URL, including the homepage, plus a human-readable body.

nginx, with the maintenance page served from a file:

# nginx: serve 503 for every request while a flag file exists
if (-f /etc/nginx/maintenance.on) {
    return 503;
}

error_page 503 /maintenance.html;

location = /maintenance.html {
    root /var/www/errors;
    add_header Retry-After 3600 always;
    internal;
}

Two details in that snippet matter. internal keeps /maintenance.html from being requestable — and therefore indexable — as a URL of its own. always on add_header is required because nginx omits added headers on non-2xx responses without it, which would silently drop your Retry-After.

Apache, via .htaccess:

# Apache: 503 everything except the maintenance page and its assets
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/maintenance\.html$
RewriteCond %{REQUEST_URI} !\.(css|png|svg)$
RewriteRule ^ /maintenance.html [R=503,L]
Header always set Retry-After "3600"
ErrorDocument 503 /maintenance.html

Be careful with the exclusion conditions. A rule that excludes too little produces a maintenance page with no styling; a rule that excludes too much leaves parts of the application reachable in a half-deployed state. Test the exclusion list against a real request log before you rely on it.

Retry-After and what it is actually for

Set it if you have a genuine estimate. A crawler can use it to schedule the next attempt rather than guessing, and a value that turns out to be roughly accurate is more useful than an ambitious one.

If you do not know, omit the header rather than inventing a number. An unset Retry-After falls back to the crawler’s own backoff, which is a reasonable default. A Retry-After: 60 that is still wrong four hours later is worse than nothing, because you have made a claim and broken it.

Do not set it to a value measured in days. At that duration you are no longer describing maintenance, and you should be planning the outage differently.

Things that break during a 503 window

Your robots.txt. If the maintenance rule catches /robots.txt, the crawler gets a 503 for it. Documented behaviour is that a 5xx on robots.txt causes the crawler to back off from the whole site — which during a short outage is arguably fine, but it also means the crawler is not fetching anything, including the pages you want re-checked the moment you come back. Exclude robots.txt from the maintenance rule and serve it normally.

Your monitoring. Uptime checks will fire. Suppress them deliberately rather than letting the alert channel fill with noise, because the one alert you need to see is the one that says the 503 is still on after the window closed.

The flag file. A maintenance mode controlled by a file that someone has to remember to delete will, eventually, be left on. Put the removal in the deploy script, not in a runbook step.

After you come back

Two things are worth doing, and one thing is worth not doing.

Verify a real 200 from outside. Not from inside the network, and not on the homepage only — check a deep URL, a static asset, and robots.txt. Edge caches can hold a 503 past the point where the origin has recovered.

Check that no Retry-After is still being emitted. A header set globally rather than on the error path will keep appearing on healthy responses.

Do not request re-indexing of everything. Coverage is retained across short outages by design; there is nothing to recover, and mass re-submission is effort spent against a problem that did not occur.

Where a 503 window is part of a larger URL change rather than a pure outage, the outage is the least of it — the sequencing that matters is in preserving links through a site migration, and the 503 is simply the code you serve during the gap.