The classic fix for a 503 maintenance page is a few lines of PHP that send the header. That fix fails when PHP is the thing you are taking offline. Google Search Central reminded sites on LinkedIn to return a 503 during PHP patch downtime. John Mueller added that the response can come from outside PHP, from DNS pointed elsewhere, or from a CDN (Search Engine Journal). This guide turns that advice into a setup you can test before the outage starts.
All configs below are illustrative and untested on your stack. Try them in staging first.
Why the status code matters
Google's post says a 503 preserves already indexed URLs in Search while you work. Once you serve 200 responses again, things return to normal. The post also warns against generic 404s and 200 error pages. In a 2013 post, Mueller wrote that an error page returned with a 200 status causes Google to index the error content.
Mueller's Bluesky advice was to use a 503 instead of letting responses time out, return 404s, or return 500s. A Bluesky user ranked responses from best to worst as 503, 500, 403, 404 and 200. Mueller called that "a good model."
Where the 503 can come from
The 503 has to come from something that keeps running while PHP is down. Three options fit three setups.
CDN or edge rule. This suits a site that already sits behind a CDN. Match every path except robots.txt, and return a 503 with a Retry-After header from the edge. Your origin can then be fully stopped. Confirm that your CDN lets you set a custom status code and header, and that it won't cache the 503 past your window.
Web server config. This suits a single nginx or Apache host where the web server stays up and only PHP-FPM or mod_php stops. The server answers 503 directly and never calls PHP.
DNS-pointed static host. This suits a rebuild or reboot of the origin server itself. Point DNS at a temporary server that returns 503 for everything except robots.txt. Two cautions apply. DNS TTLs delay the switch in both directions, and some static hosts can't set arbitrary status codes. Confirm yours can before you commit.
Worked audit: a hypothetical PHP site
Take a made-up WordPress site, example.com, on nginx with PHP-FPM, behind a CDN. A critical PHP fix needs a restart and a version bump. The team expects the work to take about three hours.
Before state
Run these checks against the live site and record the results.
| URL | Status | Notes | |---|---|---| | / | 200 | Served by PHP | | /guides/php-patching/ | 200 | Deep URL, served by PHP | | /robots.txt | 200 | Virtual file generated by WordPress |
The last row is the trap. WordPress generates a virtual robots.txt through PHP, so it will fail once PHP stops. Save the live output to a real file first:
curl -s https://example.com/robots.txt -o /var/www/maintenance/robots.txt
Google's pause guide, as Search Engine Journal reports it, says not to return a 503 for robots.txt, because that blocks all crawling. The carve-out is not optional.
The 503 setup at the server
This nginx sketch (illustrative) serves a static robots.txt with a 200 and returns a 503 for everything else:
server {
listen 443 ssl;
server_name example.com;
root /var/www/maintenance;
location = /robots.txt {
default_type text/plain;
try_files /robots.txt =404;
} For more on this, see [more on how to build an html sitemap that helps users and seo](/how-to-build-an-html-sitemap-that-helps-users-and-seo).
location / {
return 503;
}
error_page 503 /maintenance.html;
location = /maintenance.html {
internal;
add_header Retry-After 10800 always;
add_header Cache-Control "no-store" always;
}
}
An Apache equivalent (illustrative, needs mod_rewrite and mod_headers) follows the same logic:
RewriteEngine On
RewriteCond %{REQUEST_URI} !^/robots\.txt$
RewriteRule ^ - [R=503,L]
ErrorDocument 503 /maintenance.html
Header always set Retry-After "10800"
In Apache, test whether the Retry-After header also attaches to the robots.txt response. If it does, scope it with an environment variable or a separate block.
At a CDN, build the same rule in the vendor's interface. If the path is not /robots.txt, respond with status 503, a short HTML body, and a Retry-After header. Serve robots.txt from the CDN or from a still-running origin. Menu names differ by vendor, so this guide doesn't name them.
What Retry-After should say
Google's crawl rate documentation says a 503 or 429 can carry a Retry-After header. The value is either a number of seconds or a date and time in UTC. Set it to roughly your expected window rather than a wishfully short value. For a three-hour job, 10800 seconds fits. If the work runs long, raise it. Treat it as a hint to crawlers, not a guarantee of when they return.
After state
During the window, the same three URLs should read:
| URL | Status | Retry-After | |---|---|---| | / | 503 | Present | | /guides/php-patching/ | 503 | Present | | /robots.txt | 200 | Not needed |
Steps to run the maintenance window
- Copy robots.txt to a static file. Save the live output and confirm it matches what crawlers see today.
- Stage the maintenance page. Create a short, plain maintenance.html. Users will see this body, but it must still go out with a 503 status. Never serve it as a 200 error page.
- Load the config without enabling it. Run the syntax check (for nginx,
nginx -t) and keep the change ready to switch on. - Capture the before state. Run the curl checks below and save the output.
- Enable the 503 first, then stop PHP. Order matters. If PHP stops before the 503 is live, crawlers can hit timeouts or 500s.
- Verify the during state. Re-run every check.
- Patch and test privately. Use a hosts-file entry, VPN, or allowlisted IP to test the patched site before opening it to everyone.
- Disable the 503 and verify 200s. Run the checks again, then clear any CDN cache.
Curl verification routine
Request headers only, and don't follow redirects, so you see the real status:
curl -sI https://example.com/
curl -sI https://example.com/guides/php-patching/
curl -sI https://example.com/robots.txt
For a compact status check across all three:
for u in / /guides/php-patching/ /robots.txt; do
printf "%s " "$u"
curl -s -o /dev/null -w "%{http_code}\n" "https://example.com$u"
done
Read the results this way:
- Before: 200, 200, 200.
- During: 503, 503, 200.
Also read: our guide to google indexing old domain after migration? audit steps
Also confirm the Retry-After header on the homepage and deep URL with curl -sI. Check that it is absent or harmless on robots.txt.
Test a URL with a query string too, since some rules match only exact paths. If you use a CDN, run the checks twice to catch a cached 200 or a cached 503.
Rollback and post-patch checklist
- Remove the 503 rule or restore the original server block.
- Start PHP and confirm the homepage returns 200 with real content, not the maintenance page.
- Purge the CDN cache so no 503 or maintenance body lingers.
- Re-run the curl routine and compare against your before table.
- Confirm robots.txt is served by its normal source again, and diff it against the saved copy.
- Check that no 200 maintenance page leaked anywhere, such as a cached homepage.
- Watch server logs and Search Console crawl stats over the following days.
Limits to state plainly
Limitations
Keep it short. Google's crawl rate documentation recommends against serving 500, 503, or 429 for longer than 1-2 days. If Googlebot sees those codes on the same URL for multiple days, the URL may be dropped from the index.
Crawling slows hostname-wide. When crawlers meet many such errors, they reduce the crawl rate across the whole hostname, including URLs that still return content. They raise it again automatically once errors drop.
Metadata cannot refresh. Google's pause guide says its systems can't refresh titles, descriptions, metadata, or structured data on a page that returns a 503.
Longer closures need a different plan. For a closure beyond a day or two, the same guide points to an indexable home page served with a 200 as a placeholder.
None of this promises that rankings or traffic stay the same. A 503 reduces the risk of URLs being dropped during a short outage. It does not replace a fast patch.
What to expect next
After you flip back to 200s, expect crawling to pick up gradually rather than instantly. Google's status codes page describes recovery that way.
Save your curl routine and staged config. Sites on PHP 8.1 or older no longer get regular official updates. PHP has published limited source fixes for versions 7.4 through 8.1 for teams that build PHP themselves, and hosts or support vendors may supply their own.



