Your Maintenance Page Returns 200, and Search Engines Are Indexing It

Date:

Maintenance mode hides your site from visitors. It does not, by default, tell search engines to come back later — and the difference is one checkbox most people never find.

The short answer: a maintenance page should return HTTP 503 Service Unavailable, not 200. Many maintenance plugins ship with the 503 option switched off, so every URL on the site answers 200 with identical placeholder text. Turn it on before you take a site down, not after.
What the status code tells a crawlerSame placeholder page. The status code decides which of these two things happens.
Answers 200

"Here is the content you asked for, and it is correct." The crawler takes that at face value.

the wrong answer
→ placeholder indexed

Every URL now holds an identical, thin page. Real entries are replaced by the same few sentences.

cleanup after launch
Answers 503

"Temporarily unable to handle this; try later." The crawler understands the absence is temporary.

→ index preserved

Existing entries are left alone. The crawler returns after the window closes. Nothing to clean up.

Nothing errors either way. The site looks correctly hidden, because to a human it is — which is why this is worth checking rather than assuming.

Why 200 is the wrong answer

An HTTP status code is a machine-readable statement about what just happened. 200 OK means here is the content you asked for, and it is correct. 503 Service Unavailable means the server is temporarily unable to handle this; try later.

A crawler that receives 503 leaves the existing entry alone and returns later. A crawler that receives 200 takes the response at face value: this URL now contains a short page saying the site is under maintenance. Do that across every URL and you have handed the index a set of pages that are identical, thin, and wrong.

The damage is quiet. Nothing errors. The site looks correctly hidden, because to a human it is.

The damage is quiet — nothing errors, and the site looks correctly hidden.

The part that makes it worse: your sitemap is still working

Taking a site into maintenance rarely disables its XML sitemap. So the sitemap keeps returning 200 and keeps listing every published URL — while each of those URLs now returns the placeholder.

That is not a passive risk. A sitemap is an invitation: it exists to tell crawlers which URLs to fetch. On the site that prompted this article, the sitemap was offering 69 URLs, and every one of them answered 200 with the same maintenance body.

A sitemap is an invitation — and this one was inviting crawlers to 69 identical placeholders.

How to check in ten seconds

Fetch your own homepage without a browser, so no cache or login gets in the way:

curl -s -o /dev/null -w "%{http_code}\n" https://example.com/Code language: Bash (bash)

Anything other than 503 while maintenance is on is the bug. Check a deep URL too — an article, a category page — because some setups only handle the homepage:

curl -sI https://example.com/some/real/article/ | head -1Code language: Bash (bash)

You want to see HTTP/1.1 503 Service Unavailable, ideally with a Retry-After header — checking the status your server actually returns is the same habit as auditing the rest of your HTTP responses.

Fixing it

Most maintenance plugins have the setting; it is usually worded as something like “enable 503 status code to discourage search engine crawlers” and it is usually off by default. Turn it on and re-run the check above.

That default deserves a moment’s thought. A plugin whose only job is removing a site from public view ships with the mechanism that tells search engines it has been removed switched off. The safe behavior exists, costs one click, and is opt-in — while the harmful state is what you get from installing the plugin and turning it on, which is the entire user journey. When you evaluate any plugin, the defaults are part of the product.

The safe behavior costs one click and ships opt-in by default.

When the setting does not seem to work

If you tick the box and the status stays 200, the setting is probably not reaching the code. Before assuming the plugin is broken, check that you are changing the option the plugin actually reads.

This bites more often than it should, because plugins change hands and rename things. A site can end up with two option rows that look equally plausible: one left by an older version, one used by the current one. Editing the wrong one changes nothing, and nothing tells you so.

Two habits make this quick:

  • Search the source for the setting name, not the option name. If the saved data contains a key the code never mentions, that key is dead — a leftover from a rename.
  • Follow the accessor. If the plugin’s own getter returns something like a nested slice of its options rather than the whole array, the live option is nested. A flat option with the same field names is a different, older row.

A useful intermediate signal: if your change causes some observable difference — different cache headers, say — but not the one you wanted, you are touching live code with the wrong value, rather than editing a dead row. That distinction tells you which of the two problems you have.

When the setting cannot be saved at all

In October 2026 our own pre-launch wall turned out to be serving 200, and this time the option row was not the problem. With the Maintenance plugin by WebFactory, version 4.32, the settings screen shows “Enable 503 (Service Unavailable) status code to discourage Search Engine crawlers”; we ticked it and saved, twice, and it came back unticked each time. Reading the plugin’s code on a test site explained why: the routine that saves the settings rebuilds the options from a fixed list of fields, and the 503 field isn’t on that list. The checkbox is displayed but never stored, and every save resets it.

Because the plugin loads its settings before most snippets run, a filter on the stored option was too late. What worked was setting the value through the plugin’s own settings API early in the request, before it serves the wall:

// Maintenance (WebFactory) 4.32: its save routine drops the 503 field, so set it ourselves.
add_action( 'init', function () {
    global $wf_mtnc;
    if ( ! is_object( $wf_mtnc ) || ! method_exists( $wf_mtnc, 'get_options' ) ) {
        return;
    }
    $o = $wf_mtnc->get_options();
    if ( is_array( $o ) && empty( $o['blockse'] ) ) {
        $o['blockse'] = '1';
        $wf_mtnc->update_options( 'options', $o ); // writes once, and again after any settings Save
    }
} );
Code language: PHP (php)

After that, every page answered 503 Service Unavailable with Retry-After: 3600, while robots.txt and the logged-in site were unaffected. The plugin sends the header itself once the value is set. The snippet is a stopgap, so it goes when the plugin goes at launch. The general lesson matches the one above: trust the status line, not the checkbox. Re-run the check after every save.

One more gap to know about: a maintenance page covers only what the theme renders. The REST API at /wp-json/ keeps serving your posts to anyone who asks, so if the site has to stay private rather than briefly down, lock the API down as well.

Getting back out of it

Turning maintenance off restores 200 responses, and crawlers pick that up on their next visit. If the site was in maintenance long enough for placeholder pages to be indexed, resubmit your sitemap and watch coverage for a week or so; the 503 approach avoids that cleanup entirely, which is the point of setting it before you go down rather than after — getting the response right up front is the same discipline as fixing any other failed protocol check.

Set the 503 before you go down; that beats cleaning up after.

The short version

Maintenance mode is two separate jobs: hide the site from people, and tell machines the absence is temporary. Most plugins do the first by default and leave the second to you. Check the status code before you rely on it — the whole verification is one curl command, and the failure mode is completely silent.

Frequently Asked Questions

Does a 503 hurt my search rankings?

Not for a temporary outage — that is precisely what the code is for. Search engines are designed to treat 503 as “come back later” and preserve the existing entry. Sustained 503s over weeks are a different matter, but that is no longer a maintenance window.

Should the XML sitemap also return 503?

It does not need to. Leaving the sitemap reachable lets crawlers keep an accurate picture of your URL set, and once each of those URLs returns 503 there is nothing harmful to fetch. The problem was never the sitemap; it was the 200s behind it.

What is the Retry-After header for?

It tells the crawler roughly how long to wait, in seconds or as a date. It is optional, but sending it is more informative than a bare 503 and costs nothing — most plugins that send 503 set it for you.

Will 503 lock me out of my own site?

No. Maintenance plugins exempt logged-in users, so the administrator continues to see the real site. The 503 is sent only on the anonymous path.

Why does my maintenance page still return 200 after enabling the option?

Most often the option you changed is not the one the code reads — commonly after a plugin has changed ownership or been renamed, leaving an older settings row behind. Search the plugin’s source for the setting name and check whether its own getter returns a nested structure.

How do I know a maintenance page was indexed?

Search for a distinctive phrase from the placeholder, restricted to your domain, or check the coverage report in your search console. If the placeholder text appears as the description for real article URLs, they were fetched and indexed during the outage.

Share post:

Subscribe

Popular

More like this
Related

How to Merge Spreadsheets by URL (and Why Our Tool Reported 5 Misses That Weren’t)

Matching two spreadsheets on a URL column is the same job as matching a stock list to a price list by product code. It goes wrong in the same two places: how you clean the code, and how you count what didn't match.

Many-to-One: Why a Mapping Backlog Outgrows Its Target Taxonomy

You're mapping a messy source vocabulary onto a clean target taxonomy. The target has about 5,600 slots, so the job must be roughly 5,600 decisions — right? The backlog is already past 8,900, and still climbing.

The Fix That Didn’t Take: When a Content-Hash Gate Skips Your Code Change

You shipped a fix to how one field is computed, re-ran the pipeline across everything, and it reported a clean run. Then the consistency check still flags those records as wrong. The fix is in the code, the rebuild "ran" — and the records never changed. Your own optimisation skipped them.

Your Dedup Scan Is Over-Counting — Because It Forgot What You Already Fixed

The report said 80 duplicate groups still to resolve. You'd been resolving them for days. You run it again after a big batch and it *still* says 80-ish. The backlog isn't stubborn — the scan is counting work you already finished.