2026年10月20日

2026年10月20日

WordPressの画像最適化エラーを解決する方法

はじめに

WordPressにアップロードした画像のファイルサイズが数MBあってページ読み込みが遅い・WebP形式への変換が機能しない・投稿に画像を挿入するとサムネイルが表示されない・大きな画像をアップロードしようとするとPHPのメモリエラーで失敗するといった問題は、GDライブラリやImageMagickの設定不備・PHPメモリ制限・画像処理フックの設定漏れが原因です。

症状・原因

  • アップロードした画像が元のサイズのまま表示されてページ速度に影響している
  • add_image_size()で登録したカスタムサイズのサムネイルが生成されない
  • WebP変換プラグインを使っているが.webpファイルが作成されない
  • 1MB以上の画像をアップロードすると「HTTPエラー」または「メモリ不足」が発生する

解決手順

ステップ1:画像処理環境を診断する

# WP-CLIで画像処理ライブラリを確認
wp eval "
\$editor = wp_get_image_editor(ABSPATH . 'wp-includes/images/blank.gif');
if (is_wp_error(\$editor)) {
    echo 'Image editor error: ' . \$editor->get_error_message() . PHP_EOL;
} else {
    echo 'Image editor: ' . get_class(\$editor) . PHP_EOL;
}
// GDライブラリ確認
if (function_exists('gd_info')) {
    \$gd = gd_info();
    echo 'GD version: ' . \$gd['GD Version'] . PHP_EOL;
    echo 'WebP support: ' . (isset(\$gd['WebP Support']) && \$gd['WebP Support'] ? 'yes' : 'no') . PHP_EOL;
}
// ImageMagick確認
if (class_exists('Imagick')) {
    \$im = new Imagick();
    echo 'ImageMagick version: ' . \$im->getVersion()['versionString'] . PHP_EOL;
    echo 'WebP support: ' . (in_array('WEBP', \$im->queryFormats()) ? 'yes' : 'no') . PHP_EOL;
}
"

# アップロード済み画像のサムネイル状況確認
wp eval "
\$attachments = get_posts(['post_type' => 'attachment', 'posts_per_page' => 5, 'post_mime_type' => 'image']);
foreach (\$attachments as \$att) {
    \$meta = wp_get_attachment_metadata(\$att->ID);
    \$sizes = isset(\$meta['sizes']) ? count(\$meta['sizes']) : 0;
    echo \$att->post_title . ': ' . \$sizes . ' サイズ' . PHP_EOL;
}
"

# PHPメモリ設定確認
wp eval "
echo 'memory_limit: '       . ini_get('memory_limit') . PHP_EOL;
echo 'upload_max_filesize: ' . ini_get('upload_max_filesize') . PHP_EOL;
echo 'post_max_size: '       . ini_get('post_max_size') . PHP_EOL;
echo 'max_execution_time: '  . ini_get('max_execution_time') . PHP_EOL;
"

ステップ2:PHPメモリとアップロード制限を修正する

// wp-config.php: メモリ制限を増やす
define('WP_MEMORY_LIMIT', '256M');
define('WP_MAX_MEMORY_LIMIT', '512M');
# .htaccess: PHPの制限を緩和
php_value memory_limit 256M
php_value upload_max_filesize 64M
php_value post_max_size 64M
php_value max_execution_time 300
// functions.php: 画像の最大サイズを設定
add_filter('big_image_size_threshold', function(int $threshold): int {
    // デフォルト2560px → 1920pxに変更
    return 1920;
});

// アップロード時の画質設定
add_filter('jpeg_quality', function(): int {
    return 82;  // デフォルト82、ファイルサイズと品質のバランス
});

add_filter('wp_editor_set_quality', function(): int {
    return 82;
});

ステップ3:サムネイルを再生成する

# 全画像のサムネイルを再生成
wp media regenerate --yes

# 特定の画像だけ再生成
wp media regenerate 123 456 789

# 特定のサイズだけ再生成(--image_sizeは非公式オプション)
wp eval "
\$attachments = get_posts(['post_type' => 'attachment', 'posts_per_page' => -1, 'post_mime_type' => 'image']);
foreach (\$attachments as \$att) {
    \$file = get_attached_file(\$att->ID);
    if (\$file && file_exists(\$file)) {
        \$meta = wp_generate_attachment_metadata(\$att->ID, \$file);
        wp_update_attachment_metadata(\$att->ID, \$meta);
        echo 'Regenerated: ' . \$att->post_title . PHP_EOL;
    }
}
"
// functions.php: カスタムサムネイルサイズを登録
add_action('after_setup_theme', function(): void {
    add_theme_support('post-thumbnails');

    // カスタムサイズ追加
    add_image_size('hero',        1920, 600, true);   // ヒーロー画像(クロップ)
    add_image_size('card',         400, 300, true);   // カード画像
    add_image_size('og-image',    1200, 630, true);   // OGP画像

    // 不要なデフォルトサイズを削除してサーバー負荷軽減
    add_filter('intermediate_image_sizes_advanced', function(array $sizes): array {
        unset($sizes['medium_large']);  // 768px幅(通常不要)
        return $sizes;
    });
});

ステップ4:WebP自動変換を実装する

// functions.php: WebP変換とフォールバック配信

// ① アップロード時にWebPを自動生成
add_filter('wp_generate_attachment_metadata', function(array $meta, int $attachment_id): array {
    $file = get_attached_file($attachment_id);
    if (!$file || !in_array(strtolower(pathinfo($file, PATHINFO_EXTENSION)), ['jpg', 'jpeg', 'png'], true)) {
        return $meta;
    }

    $webp_file = preg_replace('/\.(jpe?g|png)$/i', '.webp', $file);

    // GDを使ってWebP生成
    if (function_exists('imagewebp')) {
        $image = match(true) {
            str_ends_with(strtolower($file), '.png') => imagecreatefrompng($file),
            default => imagecreatefromjpeg($file),
        };
        if ($image) {
            imagewebp($image, $webp_file, 82);
            imagedestroy($image);
        }
    }
    return $meta;
}, 10, 2);

// ② srcsetにWebPを追加
add_filter('wp_calculate_image_srcset', function(array $sources): array {
    foreach ($sources as &$source) {
        $webp = preg_replace('/\.(jpe?g|png)$/i', '.webp', $source['url']);
        $webp_path = str_replace(
            content_url(),
            WP_CONTENT_DIR,
            $webp
        );
        if (file_exists($webp_path)) {
            $source['url'] = $webp;
        }
    }
    unset($source);
    return $sources;
});

// ③ picture要素でWebPとフォールバックを出力
add_filter('the_content', function(string $content): string {
    return preg_replace_callback(
        '/<img([^>]+src=["\']([^"\']+\.(?:jpe?g|png))["\'][^>]*)>/i',
        function(array $m): string {
            $webp = preg_replace('/\.(jpe?g|png)$/i', '.webp', $m[2]);
            $webp_path = str_replace(content_url(), WP_CONTENT_DIR, $webp);
            if (!file_exists($webp_path)) {
                return $m[0];
            }
            return sprintf(
                '<picture><source srcset="%s" type="image/webp"><img%s></picture>',
                esc_url($webp),
                $m[1]
            );
        },
        $content
    );
});

ステップ5:画像の遅延読み込みとCLSを防止する

// functions.php: 画像パフォーマンス最適化

// ① width/heightを自動付与してCLSを防ぐ
add_filter('wp_get_attachment_image_attributes', function(array $attr, WP_Post $attachment, $size): array {
    // loading=lazyを確保
    if (!isset($attr['loading'])) {
        $attr['loading'] = 'lazy';
    }
    // decoding=asyncを追加
    $attr['decoding'] = 'async';

    // width/heightが未設定の場合は補完
    if (!isset($attr['width']) || !isset($attr['height'])) {
        $image_data = wp_get_attachment_image_src($attachment->ID, $size);
        if ($image_data && count($image_data) >= 3) {
            $attr['width']  = $image_data[1];
            $attr['height'] = $image_data[2];
        }
    }
    return $attr;
}, 10, 3);

// ② アイキャッチ画像はfetchpriority=highで先読み
add_action('wp_head', function(): void {
    if (!is_singular() || !has_post_thumbnail()) {
        return;
    }
    $thumbnail_id  = get_post_thumbnail_id();
    $thumbnail_url = wp_get_attachment_image_url($thumbnail_id, 'large');
    if ($thumbnail_url) {
        printf(
            '<link rel="preload" as="image" href="%s" fetchpriority="high">%s',
            esc_url($thumbnail_url),
            PHP_EOL
        );
    }
}, 1);
# 未使用の添付ファイルを削除してディスクを節約
wp eval "
\$orphans = get_posts([
    'post_type'      => 'attachment',
    'post_parent'    => 0,
    'posts_per_page' => -1,
    'post_status'    => 'inherit',
]);
echo '未添付ファイル数: ' . count(\$orphans) . PHP_EOL;
"

# 画像ファイルのサイズ確認
wp eval "
\$upload_dir = wp_upload_dir();
echo 'アップロードディレクトリ: ' . \$upload_dir['basedir'] . PHP_EOL;
"

注意事項

  • wp media regenerateはサーバーに大きな負荷をかけます。記事数が多いサイトでは深夜に実行するか、--limitオプションで分割して実行してください
  • WebP変換を有効にする場合、GDライブラリのWebPサポートが有効になっている必要があります。phpinfo()でGD Supportセクションを確認してください
  • big_image_size_thresholdフィルターを無効化(return false)すると、元の巨大な画像がそのまま保存されます。ストレージ容量に注意してください

まとめ

WordPress画像最適化の解決は①wp_get_image_editor()でGD/ImageMagick確認・WebPサポート状況・PHPメモリ制限の確認、②WP_MEMORY_LIMIT=256M設定・jpeg_qualityフィルターで画質調整・big_image_size_thresholdで最大サイズ制限、③wp media regenerateで全サムネイル再生成・add_image_size()でカスタムサイズ登録・intermediate_image_sizes_advancedで不要サイズ削除、④wp_generate_attachment_metadataフックでWebP自動生成・wp_calculate_image_srcsetにWebP追加・picture要素でフォールバック配信、⑤loading=lazy・decoding=async・width/height属性でCLS防止・アイキャッチにfetchpriority=highでLCP改善の手順で解決します。

お気軽にご相談ください

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