Simple WordPress Maintenance Mode Without a Plugin

You need WordPress maintenance mode without a plugin: visitors should see a short “back soon” page while you work, and you, logged in as an administrator, should keep seeing the real site. This snippet does that with a proper 503 Service Unavailable status, so search engines treat the downtime as temporary instead of indexing the maintenance message.

The snippet

<?php
/**
 * Maintenance mode: visitors get a 503 page, administrators see the site.
 */
add_action( 'template_redirect', 'dpc_maintenance_mode', 1 );

function dpc_maintenance_mode(): void {
	if ( current_user_can( 'manage_options' ) ) {
		return;
	}

	if ( ! headers_sent() ) {
		header( 'Retry-After: 3600' );
	}

	wp_die(
		'<h1>' . esc_html__( 'Maintenance', 'dpc' ) . '</h1><p>' . esc_html__( 'This site is under scheduled maintenance. Please check back later.', 'dpc' ) . '</p>',
		esc_html__( 'Maintenance', 'dpc' ),
		array( 'response' => 503 )
	);
}

How it works

  • template_redirect fires on every front-end request just before WordPress picks a template, in classic and block themes alike. The older One-Click Import version hooked into get_header, which block themes never call, so their sites stayed public.
  • current_user_can( 'manage_options' ) lets administrators through. It returns false for logged-out visitors, so no separate login check is needed.
  • wp_die() with 'response' => 503 sends the status code and no-cache headers. The Retry-After header tells crawlers to come back in an hour; it is only sent when headers are still open, so it never raises a warning.
  • The message is escaped and translatable, and the status is an integer, as wp_die() expects.
  • wp-admin, wp-login.php, the REST API, admin-ajax and WP-Cron never fire template_redirect, so you can still log in and scheduled tasks keep running.

Add it with Scripts Organizer

In Scripts Organizer go to Code Blocks → Add New and give the block a name. Set Trigger location to Everywhere, tick PHP under Script location, paste the code into the PHP editor and publish. Leave Action/Hook empty. The function is attached to template_redirect and only runs there, when WordPress already knows who is logged in. To end maintenance, disable the code block; there is nothing to clean up.

The same code also works unchanged in a small plugin or an mu-plugin.

Customise it

  • Let editors in too: change the capability to edit_posts.
  • Longer work: raise 3600 (seconds) in the Retry-After header.
  • Keep one page public, such as a contact page: add if ( is_page( 'contact' ) ) { return; } after the capability check.
  • Own design: replace the wp_die() call with status_header( 503 );, nocache_headers();, your own markup and exit;.

To also keep wp-admin closed to everyone but you while you work, combine it with Lockdown Admin Backend to your IP.

Gotchas

  • Page caches: a caching plugin or host cache may keep serving cached pages to visitors. Purge the cache after you enable the code block, and again after you disable it.
  • Keep it short. A 503 is fine for hours; weeks of 503 responses can still cost you indexed pages, and then a real maintenance plugin with a scheduled end is the better tool.
  • Logged-in users without manage_options (customers, subscribers) see the maintenance page too. That is intended.
  • WordPress’s own .maintenance file, written during core updates, is a separate mechanism and is not affected.

In the Scripts Organizer library Import it with one click from Scripts Organizer.

// newsletter

Three tested WordPress snippets a week

Blocks, security, performance, Tailwind, PHP and React. Copy, paste, ship. You’ll also get DPlugins product updates. No spam, and you can unsubscribe anytime.

// newsletter

Three tested WordPress snippets a week

Blocks, security, performance, Tailwind, PHP and React. Copy, paste, ship. You'll also get DPlugins product updates. No spam, and you can unsubscribe anytime.