WordPress.org

WordPress Developer Blog

On-brand maintenance mode for WordPress Block Themes

On-brand maintenance mode for WordPress Block Themes

When WordPress runs updates to core, plugins, or themes it briefly goes into maintenance mode. Visitors are greeted with a plain, unstyled message: “Briefly unavailable for scheduled maintenance. Check back in a minute.” It works, but it’s a long way from the polished experience your brand deserves.

For block themes, there’s a clean solution that splits the work sensibly. First, a hook to the theme. From there, the maintenance page is created and managed entirely in the Site Editor. No file editing, no code, just blocks. Maintenance mode is activated and deactivated in whatever way suits your workflow best.

The hook

Add the following to your theme’s functions.php. This is a one-time task, once it’s in place, no code changes are needed to manage maintenance mode.

The minimal hook approach

This is fully functional for most sites doing routine update windows:

/**
* Serve the template titled "Maintenance" if the template exists.
*
* @param string $template The path to the template.
* @return string The maintenance template path, or the original template.
*/
function your_theme_force_maintenance_template( $template ) {
   if ( is_user_logged_in() ) {
       return $template;
   }

   if ( ! current_theme_supports( 'block-templates' ) ) {
       return $template;
   }

   // Look for template(s) titled exactly "Maintenance".
   $maintenance_posts = get_posts(
       array(
           'post_type'      => 'wp_template',
           'title'          => 'Maintenance',
           'post_status'    => array( 'publish', 'draft' ),
           'posts_per_page' => -1,
           'no_found_rows'  => true,
       )
   );

   $maintenance_post = null;

   foreach ( $maintenance_posts as $post ) {
       $theme_slugs = wp_get_post_terms( $post->ID, 'wp_theme', array( 'fields' => 'names' ) );
       if ( in_array( get_stylesheet(), $theme_slugs, true ) ) {
           $maintenance_post = $post;
           break;
       }
   }

   if ( ! $maintenance_post ) {
       return $template;
   }

   $slug = $maintenance_post->post_name;

   return locate_block_template( $template, $slug, array( $slug ) );
}
add_filter( 'template_include', 'your_theme_force_maintenance_template', 99 );

locate_block_template() checks for a maintenance template in the right order of precedence, and it checks the database first. That means a template created in the Site Editor will be found, provided it uses the name maintenance. Any logged-in user bypasses maintenance mode entirely, so your team can review the live site during an update without hitting the maintenance page.

Maintenance mode template actively loading for non-logged in users.
An example of a custom maintenance mode template created in Twenty Twentyfour.

The SEO-friendly headers approach

If your site has meaningful search traffic or maintenance windows tend to run longer, it’s worth adding three headers that tell search engine crawlers the downtime is temporary:

/**
* Serve the template titled "Maintenance" if the template exists.
* Includes headers to signal temporary unavailability to search engines.
*
* @param string $template The path to the template.
* @return string The maintenance template path, or the original template.
*/
function your_theme_force_maintenance_template( $template ) {
   if ( is_user_logged_in() ) {
       return $template;
   }

   if ( ! current_theme_supports( 'block-templates' ) ) {
       return $template;
   }

   // Look for template(s) titled exactly "Maintenance".
   $maintenance_posts = get_posts(
       array(
           'post_type'      => 'wp_template',
           'title'          => 'Maintenance',
           'post_status'    => array( 'publish', 'draft' ),
           'posts_per_page' => -1,
           'no_found_rows'  => true,
       )
   );

   $maintenance_post = null;

   foreach ( $maintenance_posts as $post ) {
       $theme_slugs = wp_get_post_terms( $post->ID, 'wp_theme', array( 'fields' => 'names' ) );
       if ( in_array( get_stylesheet(), $theme_slugs, true ) ) {
           $maintenance_post = $post;
           break;
       }
   }

   if ( ! $maintenance_post ) {
       return $template;
   }

   $slug = $maintenance_post->post_name;

   $maintenance_template = locate_block_template( $template, $slug, array( $slug ) );

   if ( ! empty( $maintenance_template ) ) {
       nocache_headers();
       status_header( 503 );
       header( 'Retry-After: 3600' );
       return $maintenance_template;
   }

   return $template;
}
add_filter( 'template_include', 'your_theme_force_maintenance_template', 99 );

status_header( 503 ) sends a “Service Unavailable” response so crawlers know the downtime is temporary rather than a permanent change. Retry-After: 3600 suggests they check back in an hour. nocache_headers() prevents the response from being stored by CDNs or proxies, so the live site is served immediately once maintenance mode is disabled. None of these affect what visitors see.

Note that in this version the headers are sent inside the template check so they are not being set unless a maintenance template is actually being served.

Creating the maintenance template

With the hook in place, the maintenance page is created entirely in the Site Editor.

Navigate to Appearance Editor Templates and add a new template. Name it Maintenance, and WordPress will assign it the slug maintenance, which is what the hook is looking for.

A maintenance mode template in the Twenty Twentyfour block theme.
Create your maintenance template in the Site Editor just like you would any other template, use patterns, template parts and more.

Build it out like any other template. It has full access to your theme’s Global Styles, so your fonts, colours, and spacing are all available. Include your header and footer template parts for the full site frame, or leave them out for a more focused page. Add a heading, a message, a logo, whatever reflects your brand.

Once saved, the template is stored in the database and the hook will find it automatically.

Activating and deactivating maintenance mode

The fastest switch is renaming the template itself. Since the hook checks for a template named maintenance, creating it (as described above) turns maintenance mode on. To turn it off, go to Appearance Editor Templates, find the Maintenance template, and rename or delete it — no file access, code, or configuration needed.

Deactivated maintenance mode in the template browser.
Deactivating maintenance mode is as easy as renaming the template.

Testing

Create the Maintenance template in the Site Editor, then log out and visit the site. You should see your maintenance page. Log back in and confirm the live site loads normally. When you’re satisfied, delete the template.

Conclusion

A maintenance page is a small detail, but it’s one visitors notice. The approach here keeps the work where it belongs: a developer adds the hook once and it stays out of the way, while the people responsible for the site’s content and brand own the maintenance page entirely through the Site Editor. No static files to manage, no code to touch when the message needs updating, just a template that’s there when you need it and gone when you don’t.

Props to @sumitsingh and @bph for reviewing this article and offering feedback.

Categories:

9 responses to “On-brand maintenance mode for WordPress Block Themes”

  1. Drivingralle Avatar

    Nice tutorial.
    Actually I would love to see a default maintenance template within core. So users can create a nice view while WordPress is doing updates.

    1. Troy Chaplin Avatar

      Thank you!

      I could see that being useful for a lot of folks, but I always question whether or not something should be part of core or if it’s better left to a plugin.

      While this code is rather easy to add, some folks aren’t comfortable or even know how to add functions. I maintain this functionality as a plugin for those who prefer that approach – https://wordpress.org/plugins/planned-outage/

  2. Weston Ruter Avatar

    Nice.

    So this isn’t related to the maintenance mode in core, right? It’s not related to wp_is_maintenance_mode().

    I see that wp_maintenance() is already sending the Retry-After: 600 and 503 status as you have. That function does try to load a wp-content/maintenance.php if it exists. It would be cool if somehow this template could also load from your maintenance block template.

    1. Weston Ruter Avatar

      It would be nice if the template path in wp_maintenance() were filterable so that plugins could provide their own template without having to write it into that drop-in location.

      1. Troy Chaplin Avatar

        I like that idea, going to look into it for my plugin, and write a follow up post if that all works out nicely!

        I originally put this together for a quick client site who wanted to have a custom branded page to download some docs while we were building out the full site in a very short timeframe.

    2. Rob Avatar
      Rob

      I agree, this is what I was expecting, given the introduction… the code snippets have no hook into wp_is_maintenance_mode() which I would think is pretty easy to include?

    3. Anh Tran Avatar

      Agree.

      The trick is great, but it would be *much* better if maintenance mode is also treated as a core template, just like 404 template, and theme can change it easily. And it should be used with all the maintenance functions/checks/hooks available in the core as well.

  3. Rob Avatar
    Rob

    I guess just add this to the top of that code snippet function:

    // Only display maintenance template when WordPress is in maintenance mode, or manually set to true
    $force_maintenance = false;
    if ( ! wp_is_maintenance_mode() && ! $force_maintenance ) {
    return $template;
    }

    …that should do it? To force maintenance mode on, change false to true.

  4. Drivingralle Avatar

    I added an issue to Gutenberg to add a default template:
    https://github.com/WordPress/gutenberg/issues/80244

Leave a Reply

Your email address will not be published. Required fields are marked *