Create Your Own Custom Maintenance Screen for WordPress

Create a custom maintenance mode page in WordPress

I recently found myself in several situations where I needed a maintenance or coming soon page (once for my site and once for a client).

I could have just used one of the many WordPress plugins, but being the stubborn girl that I am, I wanted something custom. Something simple. Something that was exactly how I wanted it.

So I set out to build my own from scratch, and today I’ll teach you how to do the same!

This also gives me the opportunity to answer Ariel’s question:

I managed to catch the maintenance screen of a blog I follow recently who operates on WordPress. She has a special maintenance page set up for this with her logo and the offer of hanging out at her facebook page while waiting for the maintenance to complete. The coding for the page itself is incredibly simple, but I can’t figure out how to make such a page to begin with. If you happen to know how to do this, could you do a tutorial on it? I would love a special maintenance page instead of their default boring one!


We’re going to build our own custom plugin. So buckle up and let’s get started!

The maintenance page is displayed to anyone not logged in.

Let’s have a brief overview of what we’ll be creating.

  • It’s a custom plugin.
  • Activating the plugin triggers the “maintenance mode”.
  • Anyone who is not an administrator and is not looking at the login page will see the maintenance page.
  • The maintenance page can be any HTML and CSS you want!

Here’s a screenshot of how mine looks:

Screenshot of Nose Graze in maintenance mode

One of the reasons that this plugin was useful for me was because many of the ones already in show the maintenance mode only to people not logged in. But on my Nose Graze Shop every single customer has an account, which means they might be logged in and would thus bypass the maintenance page!

But the custom plugin we’ll be building today shows everyone the maintenance mode unless they’re an administrator. So you specifically have to be an admin in order to bypass it.

First let’s look at the folder structure.

Before we dig into the actual coding, let’s look at the folder structure of the plugin. This will help you have an understanding of which files are doing what.

  • maintenance-mode/ — Main plugin folder containing all files.
    • assets/ — Folder containing all assets, like CSS files and images.
      • css/ — Folder containing CSS files.
        • maintenance.css — The CSS file we load in our template.
      • images/ — Folder containing images you need in your layout.
    • views/ — Folder containing all public-facing HTML layouts.
      • maintenance.php — The HTML that makes up our public-facing maintenance page.
    • maintenance-mode.php — Our main plugin file that gets things started.

Hopefully that makes sense.

  • Every single file is in a folder called maintenance-mode.
  • The maintenance-mode.php file is what tells WordPress, “Hey, this is a plugin! Let’s show maintenance mode to certain people.”
  • Any images or CSS files or JavaScript files live in the assets folder.
  • The HTML for our actual maintenance mode template goes inside the views folder, in a file called maintenance.php.

With me so far?

Create the plugin folder and main file.

First create your plugin folder. The name of the folder should be the slug of your plugin. maintenance-mode is a good choice.

Then, inside that folder, create a PHP file with the same name, but with the .php extension. So mine is called maintenance-mode.php

The code for this file is very simple:

 * Plugin Name: Maintenance Mode
 * Plugin URI:
 * Description: Displays a coming soon page for anyone who's not logged in.
 * Version: 1.0
 * Author: Nose Graze
 * Author URI:
 * License: GPL2
 * @package maintenance-mode
 * @copyright Copyright (c) 2015, Ashley Evans
 * @license GPL2+

 * Maintenance Page
 * Displays the coming soon page for anyone who's not logged in.
 * The login page gets excluded so that you can login if necessary.
 * @return void
function ng_maintenance_mode() {
	global $pagenow;
	if ( $pagenow !== 'wp-login.php' && ! current_user_can( 'manage_options' ) && ! is_admin() ) {
		header( $_SERVER["SERVER_PROTOCOL"] . ' 503 Service Temporarily Unavailable', true, 503 );
		header( 'Content-Type: text/html; charset=utf-8' );
		if ( file_exists( plugin_dir_path( __FILE__ ) . 'views/maintenance.php' ) ) {
			require_once( plugin_dir_path( __FILE__ ) . 'views/maintenance.php' );

add_action( 'wp_loaded', 'ng_maintenance_mode' );

The huge comment block as the top is what tells WordPress this is a plugin. You can edit those values to change the plugin name, URI, description, etc.

Next we have one single function that gets loaded after WordPress is loaded. This is what displays the maintenance page. This is the exact line that determines who sees the maintenance page:

if ( $pagenow !== 'wp-login.php' && ! current_user_can( 'manage_options' ) && ! is_admin() ) {

All of the following conditions must be met in order for the maintenance page to be displayed:

  • The current page must not be the login page (people need to be able to login!).
  • The current user must not have the “manage_options” capability (this is typically only given to administrators, so in other words, the user must not be an admin).
  • We must not be in the admin area. The maintenance page only affects the public portions of the site.

If all of those conditions are met, then we display the maintenance page (providing the file exists ๐Ÿ˜‰ ).

Build your maintenance mode template.

Next we’re actually going to create our template. This is what gets shown on the public side of your site.

  1. Inside the plugin folder, create another folder called views.
  2. Inside the views folder, create a new file called maintenance.php

You can put whatever HTML you want inside the maintenance.php file. Here’s what mine looks like:

 * Maintenance mode template that's shown to logged out users.
 * @package   maintenance-mode
 * @copyright Copyright (c) 2015, Ashley Evans
 * @license   GPL2+
<!DOCTYPE html>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<link rel="profile" href="">

	<link href='//,700' rel='stylesheet' type='text/css'>
	<link rel="stylesheet" href="">
	<link rel="stylesheet" href="<?php echo plugins_url( 'assets/css/maintenance.css', dirname( __FILE__ ) ); ?>">

	<title>Down for Maintenance | Nose Graze</title>


	<h1><a href="<?php echo home_url( '/' ); ?>">NoseGraze</a></h1>

	<h2>Make <strong>WordPress</strong> your bitch.</h2>

	<p>I'm making some <strong>awesome</strong> changes to the site. I'll be back shortly! Thanks for your patience. :)</p>

  • The comment block at the top describes what the page is.
  • In the <head> section I include some external stylesheets. I also include our assets/css/maintenance.css file (to be created).
  • In the <body> I include all the text I want on the page.

You can literally include whatever you want in this template. The only important thing is this line in the head:

<link rel="stylesheet" href="<?php echo plugins_url( 'assets/css/maintenance.css', dirname( __FILE__ ) ); ?>">

As I explained, this brings in our external stylesheet, which we’ll be creating next!

Create your stylesheet.

  1. Go back into the main plugin folder.
  2. Inside that, create a new folder called assets
  3. Inside “assets”, create a new folder called css
  4. If you’ll be including images in your page, then also create a folder called images in your “assets” folder.
  5. Go inside the “css” folder and create a new file called maintenance.css

The maintenance.css file is where you put all your CSS code. I’m not going to paste all of mine here since yours will obviously be different, but here’s a snippet to give you an idea:

* {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box;

html, body {
  min-height: 100%;

body {
  background: url("../images/arthur-bg.jpg") no-repeat center top;
  background-size: cover;
  color: white;
  font-family: "proxima-nova", "Source Sans Pro", "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: 18px;
  line-height: 1.6;
  padding: 2em 15px;

You can see where I’ve referenced one of my images:

background: url("../images/arthur-bg.jpg") no-repeat center top;

That’s the path you’d need to use ( ../images/your-image.jpg ).

All done!

Now literally all you have to do is add your plugin to WordPress.

  1. Turn your whole plugin folder into a zip file.
  2. Login to WordPress and go to Plugins » Add New » Upload.
  3. Upload your zip file.
  4. Activate the plugin to enable maintenance mode.

When you’re done doing maintenance, simply deactivate the plugin.

Download the ready-made plugin

I’ve put all the above code together for you in a plugin file that’s ready to activate. You can either use the template I created for you (see below) or you can modify the files to build your own and just use this plugin as a starting point.

Screenshot of Nose Graze in maintenance mode

Photo of Ashley
I'm a 30-something California girl living in England (I fell in love with a Brit!). My three great passions are: books, coding, and fitness. more »

Don't miss my next post!

Sign up to get my blog posts sent directly to your inbox (plus exclusive store discounts!).

You might like these


  1. Just perfect, Ashley!
    And I’m coming from the Make WordPress Your Bitch right now, and I have no words to thank you. Awesome! YOU ROCK!!! ๐Ÿ˜€

    1. I added a fix in my script that guesses your RSS feed URL if it’s not found automatically. ๐Ÿ™‚

  2. For some reason this works on my non-SSL staging server, but not on my SSL production server. The prod server always shows the maintenance page regardless of whether the visitor is logged in. Admin page is accessible though, and can be logged into.

    Any ideas?

    Thanks for the nifty tut!

    1. The only reason I can think of is that your production server is caching the page.

      The code is very clear that it only gets shown to people without the “manage_options” capability (administrator). So if you definitely have that, then the only reason you’d be shown the login page is if the code isn’t even being executed, which might be the case if the page is being cached and served as static HTML or something.

      1. Good tip. In the end it was user error. I had an old static index file which I thought had been deleted. Since it had not been, it was overriding the php index.

  3. Thanks, this was super-helpful for rolling my own coming soon page!

    I did notice one problem for my server, thoughโ€”the header sent was generating an internal server error (500). Obviously part of the whole point is making sure that search engines see that 503, so they know to check back later.

    This worked better for me:
    header( $_SERVER[“SERVER_PROTOCOL”] . ‘ 503 Service Temporarily Unavailable’, true, 503 );

    This makes sure we grab the server’s actual current protocol, rather than assuming it’s HTTP/1.1, so it is a little more universally functional.

  4. Is there a way to collect email addresses to inform potential customers when the website is up and running again – for example when using maintenance mode as an ‘opening soon’ page?

    1. You can put whatever code you want in the /views/maintenance.php file – including opt-in code from your email provider. ๐Ÿ™‚

  5. Ashley, thanks for this! I saw your comment about adding an email optin code, but not clear about the specific placement of that code. Where in the /views/maintenance.php file should that code be placed?

    1. Anywhere you want inside the <body> tags. Inside the <main> tags is probably what you want.

      1. Thanks for the great direction! I got the form on the maintenance page. Yay! But my background image isn’t showing. Also, trying to adjust the form to appear lower on the page. And I’m looking for your tip jar.

  6. Great, Thanks. I have to make another kind of landing page but I can use the procedure. Very Nice (y)

  7. Love this so much!!! I was able to add an email opt-in and some Font Awesome icons underneath because of the coding information you provide here and because your code is so simple and so easy to customize it’s amazing ๐Ÿ™‚ Thank you so much!!

  8. This is simply the most amazing thing ever, I got to learn to things out of one.

    Is it okay, If I say, I Love you?lol.. your the best.

      1. Thanks for this plugin!
        I replace ‘manage_options’ with what for the administrator AND the publisher to see the site?

      2. This is awsome, but for some reason I get this error:
        “PHP error, bad file/directory permissions or invalid site software configuration”
        Im new so maybe you know why it wont work ?
        Thanks for the plugin tutorial.

  9. This is awesome! Thank you!!
    Is there a way to simply redirect to a specific page (e.g., rather than the maintenance.php file? That way I could design the page as per usual (instead of manual html). You know, coz I’m lazy ๐Ÿ˜‰

    I assume you’d have to add an exception like:
    if ( $pagenow !== ‘wp-login.php’ && ! current_user_can( ‘manage_options’ ) && ! is_admin() && $pagenow !== ‘maintenance’ )
    But it’s the redirect that I can’t figure. Thanks

  10. I have an audio plugin installed elsewhere on my site, could I use shortcode to have audio player on maintenance screen via ‘do_shortcode’ etc in maintenance.php…?

  11. Hello Ashley,
    Thanks very much for your plugin. It’s really great and saved me hours of work.
    Just one question. The plugin is currently blocking xmlrpc.php as well, so you cannot use software, which is using this method. Is there a possibility to make xmlrpc.php working with your plugin?
    Thanks very much!

  12. Hi Ashley,

    thanks a lot for that blog post. I was not really satisfied with the many maintenance plugins out there and wanted a simple solution. Yours is just what I needed (acutally only the main plugin file, as I already had a static HTML page).

  13. Really helpful post, Ashley – Thank you!
    Here’s a puzzler for you… I have static client files (typically mockups & documents) served from a directory at the root level, “/clients/name-of-client/index.html”, etc.. I want the maintenance mode page to display as you’ve indicated, but to allow anonymous visitors to view anything within the “/clients/” directory if they have the correct url for it.
    What would need to be added to the code to accomplish that behavior?

  14. how to enable load a specific page for example page id 11 or page slug my-account?

  15. thaaaaaank you so much for sharing this!!! You are amazing! I’ve been trying plugins and even trying to code by myself with the maintenance.php but nothing was working for me. Finally I found your post, downloaded your plugin, edited the code with my html and it is PERFECT!!!
    Thank you, really!

  16. Ashley, thanks a lot! These are some brilliant ยปlittleยซ snippets of code!
    Being as stubborn as you are, obviously, I was looking for something I could tweak the way I want it to look. And now it does. Yeehah!

  17. This is exactly what I was looking for. Perfect for someone who understands how to make a simple custom front-end landing page, and wants to redirect traffic its way until the main WordPress site is ready to go live. Really simple result that doesn’t interfere with the admin side of things.

    Thanks so much for sharing this. Great documentation and great way to dip my toes into some custom plugin work that just works.

  18. Thank you so much for this! A maintenance page with custom HTML and CSS is exactly what I was looking for!

  19. Thank you, Ashley! Your plugin still works, and my custom maintenance page is soooo much nicer. ๐Ÿ™‚

  20. Thanks Ashley for this great opportunity to learn something very cool! ๐Ÿ™‚

    By the way is there an option to just link plugin to display existing WordPress page as Maintenance mode page?

Recent Posts

    Random Posts