[ wordpress ]
WordPress maintenance mode and SEO: how to lock a site without losing rankings
If you turn on maintenance mode on a client's WordPress site for a few hours during a deploy, Google won't notice. If you leave it on for a week, you can permanently damage the client's search rankings. The difference between the two outcomes comes down to a few HTTP headers most maintenance plugins get wrong.
This post is the short version of what every developer locking a site, for any reason, needs to know. We wrote HyperControl to do this correctly out of the box, but the reasoning applies to any maintenance solution.
What Google does when a page disappears
When Googlebot crawls a URL and gets a response, it interprets the response code as a signal about what to do next. Three responses matter for our purposes:
- 200 OK. The page exists and the content Googlebot just received is what should be in the index. If your maintenance page returns 200, Google indexes the maintenance page as the canonical content. This is the worst outcome.
- 404 Not Found or 410 Gone. The page is permanently gone. Google drops it from the index over time. Bad if your maintenance is temporary.
- 503 Service Unavailable. The page exists but is temporarily unavailable. Googlebot keeps the existing entry in the index and tries again later. This is what you want.
Most cheap maintenance plugins return 200 because they are rendering a normal WordPress page. That is the easy mistake.
The four-part recipe for a search-safe maintenance page
1. Return HTTP 503
At the very top of your maintenance handler, before any content renders, send 503 Service Unavailable. In WordPress this is one line:
status_header(503);2. Send a Retry-After header
Tell Googlebot how long to wait before crawling the URL again. The unit is seconds, or an HTTP-date. Conservative choices:
- 600 (10 minutes) for a deploy or short maintenance window.
- 3600 (1 hour) for typical plugin/theme rollouts.
- 86400 (24 hours) for prolonged outages, like a payment dispute that you expect to resolve within days.
Send it like this:
header('Retry-After: 600');3. Disable caching
Make sure the maintenance page is not cached by Cloudflare, the client's host, or the browser. WordPress has nocache_headers() for this.
4. Add noindex meta as belt-and-braces
Put a meta robots tag in the page itself:
<meta name="robots" content="noindex">The 503 response is the primary signal; this is a backup in case any layer in the response chain (CDN, host) accidentally rewrites the status code.
What about a multi-week lockdown?
Even with the four-part recipe, leaving a 503 in place for weeks sends a different signal: Googlebot reduces crawl frequency for the site, then eventually starts dropping pages. The exact threshold isn't public, but anecdotally a few days is fine, a few weeks starts to hurt.
For long disputes, the right approach is SEO-mode: serve the real site to Googlebot (verified by reverse-DNS), serve the maintenance page to humans. We treat this as a Phase 8 feature for the Agency tier because it's an edge case, but the technique is well-known and worth knowing.
What we do
The HyperControl maintenance handler returns 503, Retry-After: 600, full nocache headers, and <meta name="robots" content="noindex">by default. The page itself is generic: no agency name, no invoice details, no mention of a dispute. Public visitors see "site temporarily unavailable" and nothing else.
Logged-in WordPress admins (which is to say, your client's team) see the real site plus an overlay with the unpaid invoice and a payment link. So the SEO impact is zero-to-tiny, the client's reputation isn't hurt with their visitors, and the lever you are trying to pull lands exactly where it should: with the people who can pay you.
See also: the broader playbook for non-paying clients.