Ahosting Logo
Knowledge Base

How to Set Up a Maintenance Page Properly

The status code is what separates harmless from expensiveServed as 200Served as 503What it tells acrawlerthis is now the pagethe site is temporarily unavailableIf it lasts hourspages can be replaced in the indexnothing changesRetry-After headermeaninglesstells crawlers when to come backYour own accessblocked too, unless you allow yourselfsameWrite a page that cannot fail: static, self-contained, and not served by the application you are working on.

A maintenance page tells visitors the site is temporarily unavailable. Done properly it also tells search engines, which is the part almost everyone gets wrong, and the difference matters if the work runs longer than an afternoon.

Return 503, not 200

The single most important thing here.

A maintenance page served with a normal 200 response says "this is the page, it is fine". Search engines take that literally: they see your maintenance notice as the site's actual content and may index it in place of the real page.

503 Service Unavailable means "temporarily unavailable, come back". Crawlers keep the existing page and return later.

An afternoon of maintenance behind a 200 usually costs nothing. Two days of it can remove pages from the index, and getting them back takes far longer than the maintenance did. There is more on the wider set in understanding HTTP status codes.

Add Retry-After

A header saying when to come back, either as seconds or as a date:

Retry-After: 3600

It is a hint in place of a rule, and it tells crawlers roughly when to try again instead of guessing.

Give an honest number. A short value with the site still down means repeated crawls of an unavailable site; a very long one delays re-crawling after you are finished.

Doing it in .htaccess

The version that works without touching the application:

RewriteEngine On
RewriteCond %{REMOTE_ADDR} !^203\.0\.113\.45$
RewriteCond %{REQUEST_URI} !^/maintenance\.html$
RewriteRule ^(.*)$ /maintenance.html [R=503,L]

ErrorDocument 503 /maintenance.html
Header always set Retry-After "3600"

The first condition lets your own address through so you can see the real site while working. The second stops the maintenance page itself from being rewritten, which would otherwise produce a redirect loop.

Copy .htaccess before editing it. A mistake here takes the whole site down in a way that is not the controlled outage you intended.

Let yourself in

Whitelisting your own address is what makes maintenance mode usable in place of an act of faith.

Check what your address actually is before adding it; the one you think you have is often not the one the server sees, particularly behind a CDN, where every request arrives from the proxy instead. Setting up a CDN walks through the real-IP handling that makes the whitelist work again.

If your address is dynamic, a query-string bypass or a cookie is more reliable than an IP rule.

The page itself

Keep it plain and self-contained. Inline the styling rather than linking a stylesheet, if the work involves the web server, a linked asset may not be served, and you get an unstyled page inside your controlled outage.

Say four things: that it is temporary, roughly how long, an alternative way to reach you, and nothing else.

Do not put technical detail on it. "Upgrading the database" tells visitors nothing useful and tells anyone probing your site something they did not have.

Static, not dynamic

The maintenance page must not depend on the thing you are maintaining.

A page rendered by WordPress cannot be shown while WordPress is being upgraded. A page querying the database fails while the database is being restored.

Plain HTML, on disk, with no dependencies. That is what makes it work in the situations you actually need it.

Application maintenance modes

WordPress enters its own maintenance mode automatically during updates: a file appears, the site shows a brief notice, and the file is removed when the update finishes.

When an update fails partway, that file is left behind and the site stays in maintenance mode indefinitely. The fix is to delete the .maintenance file in the WordPress directory, and it catches people because nothing else explains it. For that, see fixing common WordPress errors.

Plugin-based maintenance modes are convenient and share the same weakness: they run inside WordPress, so they cannot help when WordPress is the problem.

Mail and other services keep running

A maintenance page affects the website. Mail, FTP and databases carry on.

Worth knowing in both directions: your customers can still email you during the outage, and a mail problem is not fixed by a maintenance page on the site.

Take it down properly

Remove the rules instead of leaving them with your address whitelisted. A site that works perfectly for you and shows a maintenance page to everyone else can persist for days, because the person checking is the one exempted.

After removing it, check from outside: a private window on mobile data, or an online checker. That is the only test that proves the outage is over.

curl -I https://example.com/

Confirm a 200 in place of a 503. If a CDN is in front, purge its cache too, or it will keep serving the maintenance page it cached, complete with the 503 that told it not to.

When not to use one

For work taking a few minutes, a maintenance page is often more disruptive than the brief errors it prevents.

For anything longer, or anything where a half-updated site would take orders it cannot fulfil, use one: on a shop, that is the whole reason it exists.

Confirm what visitors and crawlers actually receive

The status code is the whole point of a maintenance page, and it is the part that is easiest to get wrong.

curl -sI https://example.com/ | head -1
curl -sI https://example.com/some/deep/page | head -1
curl -sI https://example.com/ | grep -i retry-after

Every address must return the unavailable status, not just the home page. A site where the front page reports maintenance and deep pages return a not found error is telling search engines those pages are gone.

The retry header is what distinguishes a planned pause from a broken site. Without it, a crawler has no information about when to return and treats the outage as open ended.

Keep the page independent of what is down

A maintenance page that depends on the application cannot be shown when the application is the problem.

ls -la ~/public_html/maintenance.html
grep -c '

Write it as a single file with the styling inside it and no external references. Every reference is something else that has to work, and during maintenance the assumption that anything works is the wrong one.

The same applies to images. A logo referenced from a path that the rules are currently redirecting produces a broken image on the one page you wanted to look deliberate.

Make sure you can still get in

A rule that redirects everything redirects you too, which is discovered at the moment you need to check whether the work succeeded.

curl -s ifconfig.me; echo
curl -sI https://example.com/ | head -1
curl -sI -H 'X-Maintenance-Bypass: 1' https://example.com/ | head -1

Add an exception for your own address before enabling the rule, and test it from the network you will actually be working from. An exception for the office address does not help somebody working from home that evening.

Set a reminder to remove the page. A maintenance page left in place after the work finished is an outage that nobody is investigating, because the site is reporting exactly what it was told to report.