2026年5月17日

2026年5月17日

WordPressのメモリ制限(memory limit)エラーを解決する方法

はじめに

WordPressでFatal error: Allowed memory size of XXXXXXX bytes exhaustedというエラーが発生した場合、PHPに割り当てられているメモリが不足しています。wp-config.php、php.ini、.htaccessなど複数の方法でメモリ制限を引き上げることができますが、根本的な原因であるメモリを大量消費するプラグインやコードの最適化も並行して行うことが重要です。

症状・原因

  • 管理画面や投稿編集ページで白い画面(WSOD)が表示される
  • Fatal error: Allowed memory size of 67108864 bytes exhaustedエラーが発生する
  • 画像アップロードや一括処理(複数投稿の更新など)で突然エラーになる
  • 特定のプラグインを有効化するとメモリエラーが発生する
  • WooCommerceなどの重いプラグインを導入後からエラーが頻発するようになった

解決手順

ステップ1:メモリエラーの特定とデバッグ方法

<?php
/**
 * メモリ使用状況の確認と診断
 *
 * まずはエラーの詳細を把握することが重要
 */

// --- 現在のメモリ設定を確認 ---
function check_memory_settings() {
    echo 'PHP memory_limit: '     . ini_get( 'memory_limit' )     . '<br>';
    echo 'WP_MEMORY_LIMIT: '      . WP_MEMORY_LIMIT                . '<br>';
    echo 'WP_MAX_MEMORY_LIMIT: '  . WP_MAX_MEMORY_LIMIT            . '<br>';
    echo '現在のメモリ使用量: '   . size_format( memory_get_usage( true ) ) . '<br>';
    echo 'ピーク時メモリ使用量: ' . size_format( memory_get_peak_usage( true ) ) . '<br>';
}

// 管理画面でのみ確認する場合
if ( is_admin() && current_user_can( 'manage_options' ) && isset( $_GET['check_memory'] ) ) {
    add_action( 'admin_notices', function() {
        echo '<div class="notice notice-info"><pre>';
        check_memory_settings();
        echo '</pre></div>';
    } );
}

/**
 * エラーログでメモリエラーを検出する
 * WP_DEBUG と WP_DEBUG_LOG を有効にした後、以下のログを確認:
 * /wp-content/debug.log
 *
 * よく見るメモリエラーのパターン:
 * - "Fatal error: Allowed memory size of 67108864 bytes exhausted"  → 64MB制限
 * - "Fatal error: Allowed memory size of 134217728 bytes exhausted" → 128MB制限
 * - "Fatal error: Allowed memory size of 268435456 bytes exhausted" → 256MB制限
 *
 * バイト数から現在の制限値を計算:
 * 67108864  = 64MB  (64  * 1024 * 1024)
 * 134217728 = 128MB (128 * 1024 * 1024)
 * 268435456 = 256MB (256 * 1024 * 1024)
 */

/**
 * 現在のメモリ使用量をフッターに表示するデバッグ用コード
 * 開発環境でのみ使用(本番環境では削除すること)
 */
function show_memory_usage_in_footer() {
    if ( ! WP_DEBUG ) return;

    $memory_used  = memory_get_usage( true );
    $memory_peak  = memory_get_peak_usage( true );
    $memory_limit = ini_get( 'memory_limit' );

    printf(
        '<div style="position:fixed;bottom:0;right:0;background:#1d2327;color:#fff;padding:8px 12px;font-size:12px;font-family:monospace;z-index:9999;">
            メモリ: %s / %s (ピーク: %s)
        </div>',
        size_format( $memory_used ),
        $memory_limit,
        size_format( $memory_peak )
    );
}
add_action( 'wp_footer',    'show_memory_usage_in_footer' );
add_action( 'admin_footer', 'show_memory_usage_in_footer' );
?>

ステップ2:wp-config.phpでWP_MEMORY_LIMITを設定

<?php
/**
 * wp-config.php でのメモリ制限設定
 *
 * 設定の優先順位(高い順):
 * 1. php.ini の memory_limit
 * 2. .htaccess の php_value memory_limit
 * 3. wp-config.php の WP_MEMORY_LIMIT
 *
 * WP_MEMORY_LIMIT はWordPress内部でのみ有効で、
 * PHP本体の制限を超えることはできない
 */

// --- 通常のフロントエンド処理用メモリ制限 ---
define( 'WP_MEMORY_LIMIT', '256M' );

// --- 管理画面・WP-Cron・一括処理用メモリ制限(フロントより多く設定) ---
define( 'WP_MAX_MEMORY_LIMIT', '512M' );

/**
 * 設定値の目安:
 *
 * 128M  → 小規模サイト(プラグイン少数、シンプルなテーマ)
 * 256M  → 標準的なサイト(推奨値)
 * 512M  → WooCommerce・多数プラグイン・大量メディア処理あり
 * 1024M → 大規模ECサイト・複雑なマルチサイト
 *
 * 注意:ホスティングプランで上限が決まっていることが多い。
 * 共有ホスティングでは 128M〜256M が上限であることが多い。
 */

/**
 * WP_MEMORY_LIMIT が効いているか確認するスニペット
 * functions.php に一時的に追加して確認後削除する
 */
add_action( 'init', function() {
    if ( is_admin() && current_user_can( 'manage_options' ) ) {
        $php_limit = ini_get( 'memory_limit' );
        $wp_limit  = WP_MEMORY_LIMIT;

        // PHPの制限がWordPressの設定より低い場合は警告
        if ( wp_convert_hr_to_bytes( $php_limit ) < wp_convert_hr_to_bytes( $wp_limit ) ) {
            add_action( 'admin_notices', function() use ( $php_limit, $wp_limit ) {
                echo '<div class="notice notice-warning"><p>';
                printf(
                    'PHPのメモリ制限(%s)がWordPressの設定(%s)より低いため、WP_MEMORY_LIMITが反映されていない可能性があります。',
                    esc_html( $php_limit ),
                    esc_html( $wp_limit )
                );
                echo '</p></div>';
            } );
        }
    }
} );
?>

ステップ3:php.ini / .htaccessでメモリ制限を設定

<?php
/**
 * php.ini での設定(サーバーの php.ini を直接編集できる場合)
 *
 * ファイルの場所:
 * - Linux: /etc/php/8.x/apache2/php.ini または /etc/php.ini
 * - 場所の確認: phpinfo() の "Loaded Configuration File" を参照
 * - WP-CLI: wp cli info でパスを確認
 *
 * 設定後は Apache/PHP-FPM の再起動が必要:
 * sudo systemctl restart apache2
 * sudo systemctl restart php8.2-fpm
 */

/*
; php.ini の設定(このファイルに直接記述)
memory_limit = 256M
max_execution_time = 300
upload_max_filesize = 64M
post_max_size = 64M
*/

/**
 * .htaccess での設定(共有ホスティングで php.ini を編集できない場合)
 * WordPressルートの .htaccess に追加する
 */

/*
# .htaccess に追加するメモリ設定
# BEGIN Memory Settings
php_value memory_limit 256M
php_value max_execution_time 300
php_value upload_max_filesize 64M
php_value post_max_size 64M
# END Memory Settings
*/

/**
 * .user.ini での設定(CGI/FastCGI/PHP-FPM 環境向け)
 * WordPress のルートディレクトリに .user.ini ファイルを作成
 */

/*
; .user.ini の内容
memory_limit = 256M
max_execution_time = 300
upload_max_filesize = 64M
post_max_size = 64M
*/

/**
 * PHPコードで直接設定する方法(最終手段・制限あり)
 * wp-config.php の DB 接続情報より前に記述
 * ※ ホスティングの設定によっては効果がない場合がある
 */
@ini_set( 'memory_limit', '256M' );

/**
 * 現在の有効なメモリ制限を確認するショートコード(一時的なデバッグ用)
 */
function memory_debug_shortcode() {
    if ( ! current_user_can( 'manage_options' ) ) return '';

    return sprintf(
        '<pre>PHP memory_limit: %s
WP_MEMORY_LIMIT: %s
現在使用量: %s
ピーク使用量: %s</pre>',
        ini_get( 'memory_limit' ),
        WP_MEMORY_LIMIT,
        size_format( memory_get_usage( true ) ),
        size_format( memory_get_peak_usage( true ) )
    );
}
add_shortcode( 'memory_debug', 'memory_debug_shortcode' );
?>

ステップ4:Query Monitorでメモリを消費するプラグインを特定

<?php
/**
 * Query Monitor プラグインによるメモリプロファイリング
 *
 * Query Monitor は無料のWordPressデバッグプラグインで、
 * 各リクエストのメモリ使用量・クエリ数・処理時間を可視化できる
 * https://ja.wordpress.org/plugins/query-monitor/
 *
 * 使い方:
 * 1. Query Monitorをインストール・有効化
 * 2. 管理画面下部のデバッグバーでメモリ使用量を確認
 * 3. 「Hooks & Actions」タブでどのフックでメモリが増加するか確認
 */

/**
 * プラグインごとのメモリ消費量を測定するカスタムコード
 * mu-plugins に追加して各プラグインのメモリ影響を測定
 */
class Plugin_Memory_Profiler {

    private static $before_memory = array();
    private static $plugin_memory = array();

    public static function init() {
        // 各プラグイン読み込み前後でメモリを記録
        add_action( 'plugin_loaded', array( __CLASS__, 'record_after_load' ), PHP_INT_MAX );
    }

    public static function record_after_load( $plugin_file ) {
        $plugin_name = basename( dirname( $plugin_file ) );
        if ( '.' === $plugin_name ) {
            $plugin_name = basename( $plugin_file, '.php' );
        }

        self::$plugin_memory[ $plugin_name ] = memory_get_usage( true );
    }

    public static function output_report() {
        if ( ! current_user_can( 'manage_options' ) ) return;
        if ( ! isset( $_GET['memory_report'] ) ) return;

        echo '<pre style="background:#1d2327;color:#cdd6f4;padding:20px;margin:20px;">';
        echo "=== プラグインメモリ使用量レポート ===\n\n";

        $prev_memory = 0;
        foreach ( self::$plugin_memory as $plugin => $memory ) {
            $diff = $memory - $prev_memory;
            printf(
                "%-40s %10s (+%s)\n",
                $plugin,
                size_format( $memory ),
                size_format( max( 0, $diff ) )
            );
            $prev_memory = $memory;
        }

        echo "\n合計: " . size_format( memory_get_usage( true ) );
        echo '</pre>';
    }
}

// mu-plugins での使用例
// Plugin_Memory_Profiler::init();
// add_action( 'admin_footer', array( 'Plugin_Memory_Profiler', 'output_report' ) );

/**
 * 特定のフックでメモリ使用量を記録するシンプルな方法
 */
function log_memory_at_hook( $hook_name ) {
    add_action( $hook_name, function() use ( $hook_name ) {
        error_log( sprintf(
            '[Memory] %s: %s (peak: %s)',
            $hook_name,
            size_format( memory_get_usage( true ) ),
            size_format( memory_get_peak_usage( true ) )
        ) );
    }, PHP_INT_MAX );
}

// 主要なフックでメモリを記録(開発時のみ使用)
if ( WP_DEBUG ) {
    log_memory_at_hook( 'plugins_loaded' );
    log_memory_at_hook( 'after_setup_theme' );
    log_memory_at_hook( 'init' );
    log_memory_at_hook( 'wp' );
}
?>

ステップ5:メモリ効率を改善するコードレベルの最適化

<?php
/**
 * PHPコードのメモリ使用量を削減するベストプラクティス
 */

// =======================================================
// NG例:大量のオブジェクトをメモリに保持
// =======================================================
function bad_get_all_posts() {
    // 全投稿をWP_Postオブジェクトの配列として取得(メモリを大量消費)
    $all_posts = get_posts( array(
        'numberposts' => -1,    // 全件取得は危険
        'post_status' => 'any', // 全ステータスを対象
    ) );

    foreach ( $all_posts as $post ) {
        // 処理...
    }

    return $all_posts; // 大量のオブジェクトをメモリに保持したまま返す
}

// =======================================================
// OK例:必要な情報のみ取得してメモリを節約
// =======================================================
function good_get_post_data() {
    global $wpdb;

    // 必要なフィールドのみを直接SQLで取得(WP_Postオブジェクトを作らない)
    $post_data = $wpdb->get_results(
        "SELECT ID, post_title, post_date
         FROM {$wpdb->posts}
         WHERE post_status = 'publish'
         AND post_type = 'post'
         ORDER BY post_date DESC
         LIMIT 100",  // 必要な件数のみ
        ARRAY_A       // 連想配列で返す(オブジェクトより軽量)
    );

    return $post_data;
}

// =======================================================
// WP_Queryでのメモリ最適化オプション
// =======================================================
function optimized_wp_query_example() {
    $query = new WP_Query( array(
        'post_type'      => 'post',
        'posts_per_page' => 50,
        'no_found_rows'  => true,   // ページネーション不要時に使用(COUNTクエリを省略)
        'update_post_meta_cache' => false,  // メタキャッシュを無効(メタ不要の場合)
        'update_post_term_cache' => false,  // タームキャッシュを無効(タクソノミー不要の場合)
        'fields'         => 'ids',  // IDのみ取得(WP_Postオブジェクトを生成しない)
    ) );

    // IDのみで処理する場合
    $post_ids = $query->posts; // array(1, 2, 3, ...)

    // 必要な投稿を個別に取得
    foreach ( $post_ids as $post_id ) {
        $title = get_the_title( $post_id );
        // 処理...

        // 使い終わったデータをメモリから解放
        clean_post_cache( $post_id );
    }

    wp_reset_postdata();
}

// =======================================================
// 大量データ処理時のバッチ処理
// =======================================================
function process_posts_in_batches( $total_posts, $batch_size = 100 ) {
    $offset = 0;
    $processed = 0;

    while ( $offset < $total_posts ) {
        // バッチサイズ分ずつ処理
        $posts = get_posts( array(
            'numberposts'    => $batch_size,
            'offset'         => $offset,
            'post_status'    => 'publish',
            'fields'         => 'ids',
            'no_found_rows'  => true,
            'update_post_meta_cache' => false,
            'update_post_term_cache' => false,
        ) );

        if ( empty( $posts ) ) break;

        foreach ( $posts as $post_id ) {
            // 各投稿を処理...
            $processed++;

            // 処理後にキャッシュをクリアしてメモリを解放
            clean_post_cache( $post_id );
        }

        $offset += $batch_size;

        // PHPのガベージコレクタを明示的に呼ぶ(長時間処理の場合)
        if ( function_exists( 'gc_collect_cycles' ) ) {
            gc_collect_cycles();
        }
    }

    return $processed;
}

// =======================================================
// 不要な処理を外す(使わないフックを削除)
// =======================================================
function optimize_wp_hooks() {
    // フロントエンドでoEmbedが不要な場合は削除
    remove_action( 'wp_head', 'wp_oembed_add_discovery_links' );

    // RSSフィードが不要な場合は削除
    remove_action( 'wp_head', 'feed_links',              2 );
    remove_action( 'wp_head', 'feed_links_extra',        3 );

    // 絵文字スクリプトを削除(使わない場合)
    remove_action( 'wp_head',             'print_emoji_detection_script', 7 );
    remove_action( 'wp_print_styles',     'print_emoji_styles' );
    remove_action( 'admin_print_scripts', 'print_emoji_detection_script' );
    remove_action( 'admin_print_styles',  'print_emoji_styles' );
}
add_action( 'init', 'optimize_wp_hooks' );
?>

注意事項

  • ホスティングの上限: WP_MEMORY_LIMITはPHPのmemory_limitの上限を超えて設定しても無効です。共有ホスティングではサポートに問い合わせて上限を確認してください
  • 制限を上げるだけでは不十分: メモリを増やすことは一時的な解決策です。根本原因(メモリリークのあるプラグイン、非効率なクエリなど)を特定して修正することが重要です
  • no_found_rowsの注意点: ページネーションに必要なmax_num_pagesが取得できなくなるため、ページネーションのある一覧ページではfalseのままにしてください
  • 定期的なガベージコレクション: 長時間のバッチ処理ではgc_collect_cycles()を適切な間隔で呼び出してメモリを解放してください

まとめ

WordPressのメモリエラーはWP_MEMORY_LIMIT/WP_MAX_MEMORY_LIMIT定数、php.ini.htaccessの3段階で対応し、Query Monitorでメモリを消費するプラグインを特定、no_found_rowsfields => idsなどのクエリ最適化で根本的な改善を行うことが重要です。関連記事:WordPressのwp-config.phpを正しく設定してセキュリティを強化する方法

お気軽にご相談ください

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