2026年5月19日

2026年5月19日

WordPressにメンテナンスモードを実装する方法【IP制限・カスタムページ】

はじめに

WordPressサイトのリニューアルや大規模更新作業中でも、管理者はバックエンドにアクセスしながらユーザーにはメンテナンスページを表示させたいことがあります。wp_loadedフィルターとIPホワイトリスト、さらにトランジェントを使ったスケジュール機能まで組み合わせることで、柔軟なメンテナンスモードを実現できます。

症状・原因

  • WordPressの自動更新中に表示される標準メンテナンス画面が味気なく見栄えが悪い
  • メンテナンス中も管理者や開発者がサイトを確認できるようにしたい
  • 特定の時間帯だけ自動でメンテナンスモードにする定期スケジュールが組みたい
  • .maintenanceファイルの扱い方が分からず誤って永続的にメンテナンス状態になった
  • プラグインを使わずにセキュアなメンテナンスモードを実装したい

解決手順

ステップ1:WordPressの.maintenanceファイルによる標準メンテナンスモード

<?php
/**
 * WordPress標準の.maintenanceファイルを使ったメンテナンスモード
 *
 * 方法1:WordPressルートディレクトリに .maintenance ファイルを配置
 * ファイルの内容はPHPコードで $upgrading 変数に現在のUNIXタイムスタンプを設定
 */

// .maintenance ファイルの内容(WordPressルートに配置):
// <?php $upgrading = 1700000000; ?>

/**
 * WP-CLIで.maintenanceファイルを作成・削除するコマンド(参考)
 * 作成: wp maintenance-mode activate
 * 削除: wp maintenance-mode deactivate
 * 確認: wp maintenance-mode status
 */

/**
 * プログラムで.maintenanceファイルを作成・削除するPHP関数
 */
function create_maintenance_file() {
    $maintenance_file = ABSPATH . '.maintenance';
    $content = '<?php $upgrading = ' . time() . '; ?>';
    file_put_contents( $maintenance_file, $content );
}

function remove_maintenance_file() {
    $maintenance_file = ABSPATH . '.maintenance';
    if ( file_exists( $maintenance_file ) ) {
        unlink( $maintenance_file );
    }
}

/**
 * カスタムメンテナンスHTMLテンプレートを使用する
 * wp-content/maintenance.php が存在する場合はWordPressが自動で使用する
 */
// wp-content/maintenance.php の例:
?>
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>メンテナンス中 | <?php bloginfo( 'name' ); ?></title>
    <style>
        body { font-family: sans-serif; text-align: center; background: #f0f0f1; }
        .maintenance-box { max-width: 600px; margin: 100px auto; padding: 40px; background: white; border-radius: 8px; }
        h1 { color: #1d2327; }
        p { color: #50575e; }
    </style>
</head>
<body>
    <div class="maintenance-box">
        <h1>メンテナンス中です</h1>
        <p>現在、サイトのメンテナンス作業を行っています。</p>
        <p>しばらく経ってから再度アクセスしてください。</p>
    </div>
</body>
</html>

ステップ2:wp_loadedフィルターでカスタムメンテナンスページを表示

<?php
/**
 * wp_loaded フックを使ったカスタムメンテナンスモード
 * .maintenanceファイルより柔軟な制御が可能
 *
 * functions.php または mu-plugins に追加
 */
function custom_maintenance_mode() {
    // メンテナンスモードが有効かどうかを確認
    $maintenance_enabled = get_option( 'site_maintenance_mode', false );

    if ( ! $maintenance_enabled ) {
        return; // 無効なら何もしない
    }

    // 管理画面へのアクセスは許可
    if ( is_admin() ) {
        return;
    }

    // WP-CLIからの実行は除外
    if ( defined( 'WP_CLI' ) && WP_CLI ) {
        return;
    }

    // cron実行時は除外
    if ( defined( 'DOING_CRON' ) && DOING_CRON ) {
        return;
    }

    // XMLRPCリクエストは除外
    if ( defined( 'XMLRPC_REQUEST' ) && XMLRPC_REQUEST ) {
        return;
    }

    // メンテナンスページのHTMLを直接出力してWordPressのロードを止める
    $maintenance_message = get_option( 'site_maintenance_message',
        '現在、サイトのメンテナンス作業を行っています。しばらく経ってから再度アクセスしてください。'
    );
    $maintenance_end = get_option( 'site_maintenance_end', '' );

    // HTTPステータスコード 503 + Retry-Afterヘッダー
    header( 'HTTP/1.1 503 Service Unavailable' );
    header( 'Retry-After: 3600' ); // 1時間後に再試行
    header( 'Content-Type: text/html; charset=UTF-8' );

    // カスタムテンプレートが存在する場合はそれを使用
    $template_path = get_stylesheet_directory() . '/maintenance.php';
    if ( file_exists( $template_path ) ) {
        include $template_path;
        exit;
    }

    // デフォルトのメンテナンスHTML
    ?>
    <!DOCTYPE html>
    <html lang="ja">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>メンテナンス中</title>
        <style>
            * { box-sizing: border-box; margin: 0; padding: 0; }
            body { font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
                   background: #f0f0f1; display: flex; align-items: center; justify-content: center;
                   min-height: 100vh; }
            .card { background: white; border-radius: 12px; padding: 60px 40px;
                    max-width: 500px; width: 90%; text-align: center; box-shadow: 0 4px 24px rgba(0,0,0,.1); }
            .icon { font-size: 64px; margin-bottom: 24px; }
            h1 { color: #1d2327; font-size: 28px; margin-bottom: 16px; }
            p { color: #50575e; line-height: 1.7; margin-bottom: 16px; }
            .eta { background: #f0f0f1; border-radius: 6px; padding: 12px 20px;
                   font-size: 14px; color: #3c434a; }
        </style>
    </head>
    <body>
        <div class="card">
            <div class="icon">🔧</div>
            <h1>メンテナンス中です</h1>
            <p><?php echo esc_html( $maintenance_message ); ?></p>
            <?php if ( $maintenance_end ) : ?>
                <div class="eta">
                    終了予定:<?php echo esc_html( $maintenance_end ); ?>
                </div>
            <?php endif; ?>
        </div>
    </body>
    </html>
    <?php
    exit;
}
add_action( 'wp_loaded', 'custom_maintenance_mode' );
?>

ステップ3:IPホワイトリストで管理者アクセスを維持

<?php
/**
 * IPホワイトリストによるメンテナンスモードのアクセス制御
 * 特定のIPアドレスからはメンテナンスモードをバイパスできる
 */
function maintenance_mode_with_ip_whitelist() {
    $maintenance_enabled = get_option( 'site_maintenance_mode', false );

    if ( ! $maintenance_enabled ) return;
    if ( is_admin() ) return;

    // IPホワイトリストを取得(管理画面から設定可能にする)
    $whitelist_option = get_option( 'maintenance_ip_whitelist', '' );
    $allowed_ips      = array_filter( array_map( 'trim', explode( "\n", $whitelist_option ) ) );

    // 開発環境のIPを常に許可
    $allowed_ips[] = '127.0.0.1';
    $allowed_ips[] = '::1'; // IPv6 localhost

    // 現在のIPを取得(プロキシ経由にも対応)
    $current_ip = get_client_ip();

    // IPがホワイトリストに含まれる場合はバイパス
    if ( in_array( $current_ip, $allowed_ips, true ) ) {
        return; // ホワイトリストのIPは通常通りアクセス可能
    }

    // ログインユーザーで管理者権限があればバイパス
    if ( is_user_logged_in() && current_user_can( 'manage_options' ) ) {
        return;
    }

    // 上記以外のアクセスにはメンテナンスページを表示
    show_maintenance_page();
}

/**
 * クライアントIPを取得するヘルパー関数
 * プロキシやロードバランサー経由にも対応
 */
function get_client_ip() {
    $ip_keys = array(
        'HTTP_CF_CONNECTING_IP',    // Cloudflare
        'HTTP_X_FORWARDED_FOR',     // 一般的なロードバランサー
        'HTTP_X_REAL_IP',           // Nginx
        'REMOTE_ADDR',              // 直接接続
    );

    foreach ( $ip_keys as $key ) {
        if ( ! empty( $_SERVER[ $key ] ) ) {
            $ip = trim( explode( ',', $_SERVER[ $key ] )[0] );
            if ( filter_var( $ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE ) ) {
                return $ip;
            }
        }
    }

    return $_SERVER['REMOTE_ADDR'] ?? '0.0.0.0';
}

function show_maintenance_page() {
    header( 'HTTP/1.1 503 Service Unavailable' );
    header( 'Content-Type: text/html; charset=UTF-8' );
    echo '<h1>メンテナンス中です。しばらくお待ちください。</h1>';
    exit;
}

add_action( 'wp_loaded', 'maintenance_mode_with_ip_whitelist' );

/**
 * メンテナンスモードのON/OFF管理UIを管理画面に追加
 */
function add_maintenance_admin_bar_item( $wp_admin_bar ) {
    if ( ! current_user_can( 'manage_options' ) ) return;

    $is_active = get_option( 'site_maintenance_mode', false );
    $label     = $is_active ? '🔴 メンテナンス中' : '🟢 公開中';

    $wp_admin_bar->add_node( array(
        'id'    => 'maintenance-mode-toggle',
        'title' => $label,
        'href'  => admin_url( 'options-general.php?page=maintenance-settings' ),
    ) );
}
add_action( 'admin_bar_menu', 'add_maintenance_admin_bar_item', 100 );
?>

ステップ4:WP_INSTALLINGとwp-config.phpでの設定方法

<?php
/**
 * wp-config.php を使ったメンテナンスモード設定
 *
 * 方法1:WP_INSTALLING 定数(非推奨:内部用)
 * WordPressインストール時に使われる定数。
 * 一部の処理をスキップするため直接の流用は非推奨。
 *
 * 方法2:カスタム定数で安全に実装
 */

// wp-config.php に追加する定数の例:
// define( 'SITE_MAINTENANCE_MODE', true );          // メンテナンスモード有効化
// define( 'MAINTENANCE_ALLOWED_IPS', '203.0.113.1,203.0.113.2' ); // IPホワイトリスト

/**
 * wp-config.php の定数を使ったメンテナンスモード
 * 定数はDBへのアクセスなしで判定できるため高速
 */
function maintenance_mode_from_config() {
    // 定数が定義されていない、またはfalseなら通常通り
    if ( ! defined( 'SITE_MAINTENANCE_MODE' ) || ! SITE_MAINTENANCE_MODE ) {
        return;
    }

    // 管理画面は除外
    if ( is_admin() ) return;

    // IPホワイトリストのチェック
    if ( defined( 'MAINTENANCE_ALLOWED_IPS' ) ) {
        $allowed_ips = array_map( 'trim', explode( ',', MAINTENANCE_ALLOWED_IPS ) );
        $client_ip   = get_client_ip();

        if ( in_array( $client_ip, $allowed_ips, true ) ) {
            return;
        }
    }

    // ログイン済み管理者のチェック(初回リクエストはユーザー情報未ロードのため注意)
    // wp_loaded フックでは current_user_can() が使用可能
    if ( is_user_logged_in() && current_user_can( 'manage_options' ) ) {
        return;
    }

    header( 'HTTP/1.1 503 Service Unavailable' );
    header( 'Retry-After: 7200' );
    include get_stylesheet_directory() . '/maintenance.php';
    exit;
}
add_action( 'wp_loaded', 'maintenance_mode_from_config' );
?>

ステップ5:wp_schedule_single_eventとトランジェントで自動スケジュール

<?php
/**
 * スケジュール設定によるメンテナンスモードの自動ON/OFF
 * wp_schedule_single_event と transient を組み合わせて使用
 */

/**
 * メンテナンスモードを指定時刻にONにするスケジューリング
 * @param int $start_timestamp UNIXタイムスタンプ(開始時刻)
 * @param int $duration_seconds メンテナンス時間(秒)
 */
function schedule_maintenance_mode( $start_timestamp, $duration_seconds = 3600 ) {
    // メンテナンス開始をスケジュール
    if ( ! wp_next_scheduled( 'start_maintenance_cron' ) ) {
        wp_schedule_single_event(
            $start_timestamp,
            'start_maintenance_cron',
            array( $duration_seconds )
        );
    }

    // メンテナンス終了をスケジュール
    $end_timestamp = $start_timestamp + $duration_seconds;
    if ( ! wp_next_scheduled( 'end_maintenance_cron' ) ) {
        wp_schedule_single_event(
            $end_timestamp,
            'end_maintenance_cron'
        );
    }
}

// メンテナンス開始アクション
function start_maintenance_cron_handler( $duration_seconds ) {
    update_option( 'site_maintenance_mode', true );

    // トランジェントで終了予定時刻を記録
    set_transient(
        'maintenance_end_time',
        time() + $duration_seconds,
        $duration_seconds + 60 // メンテナンス時間 + 60秒のバッファ
    );

    // 管理者に開始通知を送信
    wp_mail(
        get_option( 'admin_email' ),
        'メンテナンスモード開始',
        'サイトのメンテナンスモードが開始されました。'
    );
}
add_action( 'start_maintenance_cron', 'start_maintenance_cron_handler' );

// メンテナンス終了アクション
function end_maintenance_cron_handler() {
    update_option( 'site_maintenance_mode', false );
    delete_transient( 'maintenance_end_time' );

    // 管理者に終了通知を送信
    wp_mail(
        get_option( 'admin_email' ),
        'メンテナンスモード終了',
        'サイトのメンテナンスモードが終了しました。サイトは公開状態に戻りました。'
    );
}
add_action( 'end_maintenance_cron', 'end_maintenance_cron_handler' );

/**
 * 管理画面からメンテナンスをスケジュールする使用例:
 *
 * // 1時間後から2時間のメンテナンスをスケジュール
 * schedule_maintenance_mode( time() + 3600, 7200 );
 *
 * // 特定の日時を指定(日本時間)
 * $jst_offset = 9 * 3600; // JSTはUTC+9
 * $start = mktime( 2, 0, 0, 12, 31, 2024 ) - $jst_offset; // 2024/12/31 02:00 JST
 * schedule_maintenance_mode( $start, 3600 ); // 1時間のメンテナンス
 */
?>

注意事項

  • 503ステータス: メンテナンスページには必ずHTTP/1.1 503 Service Unavailableヘッダーを返してください。200を返すとGooglebotがメンテナンスページをインデックスしてしまいます
  • Retry-Afterヘッダー: 503と合わせてRetry-Afterヘッダーを設定するとGooglebotに「いつ再クロールすべきか」を伝えられます
  • WP Cronの信頼性: wp_schedule_single_eventはWordPress Cronに依存するため、サイトへのアクセスがないと実行されません。重要なメンテナンスにはサーバーのcronと組み合わせてください
  • キャッシュのクリア: メンテナンスモード切り替え時はキャッシュプラグインのキャッシュをクリアしてください

まとめ

.maintenanceファイル、wp_loadedフックのカスタム実装、IPホワイトリスト、wp-config.php定数、そしてwp_schedule_single_eventスケジューラを組み合わせることで、あらゆるニーズに対応したメンテナンスモードを構築できます。関連記事:WordPressのパーマリンク設定と.htaccessを最適化する方法

お気軽にご相談ください

お見積りへ お問い合わせへ